aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMitch Bradley <wmb@firmworks.com>2018-10-26 21:07:29 +0000
committerMitch Bradley <wmb@firmworks.com>2018-10-26 21:07:29 +0000
commitbc75df0df2a196f4ac98fa478ba393c83a0101fd (patch)
tree9fe53473cded283b0f884efcd311823f3d7a0897
parent0502dd5b8e4a2d05885758ae2fcba85ae4f67db8 (diff)
downloadcforth-bc75df0df2a196f4ac98fa478ba393c83a0101fd.tar.gz
esp8266 - new GRBL sender app
-rw-r--r--build/grbl-esp8266/Makefile18
-rw-r--r--src/app/esp8266/gpio-rgb-led.fth37
-rw-r--r--src/app/esp8266/gpio-switch.fth8
-rw-r--r--src/app/grbl-esp8266/app.fth35
-rw-r--r--src/app/grbl-esp8266/grbl.fth320
5 files changed, 418 insertions, 0 deletions
diff --git a/build/grbl-esp8266/Makefile b/build/grbl-esp8266/Makefile
new file mode 100644
index 0000000..5416f51
--- /dev/null
+++ b/build/grbl-esp8266/Makefile
@@ -0,0 +1,18 @@
+# Builds a GRBL sender app for an ESP8266 module
+# See src/app/grbl-esp8266/app.fth for details.
+
+# Main application code directory
+APPPATH = $(TOPDIR)/src/app/grbl-esp8266
+
+# ESP8266-specific drivers
+# Not all are used but they are listed as dependencies just in case
+APPSRCS += $(wildcard $(TOPDIR)/src/app/esp8266/*.fth)
+
+# Generic utilities
+# Not all are used but they are listed as dependencies just in case
+APPSRCS += $(wildcard $(TOPDIR)/src/lib/*.fth)
+
+# Reuse the Makefile for the generic ESP8266 build.
+# The above setting of APPPATH overrides the generic
+# setting to build the desired application code.
+include ../esp8266/Makefile
diff --git a/src/app/esp8266/gpio-rgb-led.fth b/src/app/esp8266/gpio-rgb-led.fth
new file mode 100644
index 0000000..a283c82
--- /dev/null
+++ b/src/app/esp8266/gpio-rgb-led.fth
@@ -0,0 +1,37 @@
+\ Driver for dumb RGB LED connected to 3 GPIOs
+\ init-gpio-rgb-led sets the GPIO numbers and the drive polarity.
+
+\ init-gpio-rgb-led overrides these values
+6 value red-gpio
+5 value green-gpio
+0 value blue-gpio
+0 value led-xor-mask \ 0 for active high, 1 for active low
+
+: led! ( flag gpio# -- )
+ swap ( gpio# flag )
+ 0<> 1 and led-xor-mask xor ( gpio# 0|1 )
+ swap gpio-pin!
+;
+
+: init-gpio-rgb-led ( red# green# blue# active-high? -- )
+ if 0 else 1 then to led-xor-mask ( red# green# blue# )
+ to blue-gpio to green-gpio to red-gpio ( )
+ false gpio-output blue-gpio gpio-mode false blue-gpio led!
+ false gpio-output green-gpio gpio-mode false green-gpio led!
+ false gpio-output red-gpio gpio-mode false red-gpio led!
+;
+
+: rgb-led! ( rgb -- )
+ dup 1 and blue-gpio led! 2/
+ dup 1 and green-gpio led! 2/
+ dup 1 and red-gpio led! drop
+;
+: led: ( bits "name" -- ) create , does> @ rgb-led! ;
+0 led: black-led
+1 led: blue-led
+2 led: green-led
+3 led: cyan-led
+4 led: red-led
+5 led: magenta-led
+6 led: yellow-led
+7 led: white-led
diff --git a/src/app/esp8266/gpio-switch.fth b/src/app/esp8266/gpio-switch.fth
new file mode 100644
index 0000000..7ab9756
--- /dev/null
+++ b/src/app/esp8266/gpio-switch.fth
@@ -0,0 +1,8 @@
+\ Driver for switch connected to a GPIO
+
+3 value switch-gpio
+: init-gpio-switch ( gpio# -- )
+ to switch-gpio
+ true gpio-input switch-gpio gpio-mode
+;
+: switch? ( -- flag ) switch-gpio gpio-pin@ 0= ;
diff --git a/src/app/grbl-esp8266/app.fth b/src/app/grbl-esp8266/app.fth
new file mode 100644
index 0000000..0ff78c3
--- /dev/null
+++ b/src/app/grbl-esp8266/app.fth
@@ -0,0 +1,35 @@
+\ Application load file for GRBL sender.
+\ A Wemos D1 Mini ESP8266 board (or any ESP12-based
+\ module) connects serially to a GRBL controller.
+\ Press a pushbutton switch to start sending the
+\ file "gcode" (in the ESP FLASH filesystem) to
+\ the GRBL controller. Status is displayed on a
+\ multicolor LED and optionally on an OLED display.
+\ The LED is blue when booting, yellow when ready,
+\ green when sending, and red if the send aborted
+\ with an error.
+
+fl ../esp8266/common.fth
+
+\ GRBL sender application
+fl ${CBP}/lib/lex.fth
+fl grbl.fth
+
+: app ( -- )
+ \ If the ESP module is connected to a host via the USB serial port,
+ \ the banner will be displayed on the serial terminal at startup,
+ \ and you can get an ok prompt by typing a character quickly.
+ \ Thereby you can load a new GCode file by typing
+ \ ok rf gcode
+ \ <send file with XModem>
+
+ banner hex
+ interrupt? if quit then
+
+ \ Otherwise, the system will run the GRBL sender app
+ ['] run catch drop
+ quit
+;
+
+" app.dic" save
+
diff --git a/src/app/grbl-esp8266/grbl.fth b/src/app/grbl-esp8266/grbl.fth
new file mode 100644
index 0000000..43b071d
--- /dev/null
+++ b/src/app/grbl-esp8266/grbl.fth
@@ -0,0 +1,320 @@
+\ GBRL sender that transmits the file "gcode" when you press a button
+
+\ A multicolor LED, and optionally a small OLED display, shows the status
+
+\needs init-gpio-rgb-led fl ../esp8266/gpio-rgb-led.fth
+
+\needs init-gpio-switch fl ../esp8266/gpio-switch.fth
+: wait-switch-pressed ( -- ) begin #50 ms switch? until ;
+: wait-switch-released ( -- ) begin #50 ms switch? 0= until ;
+
+\ Driver to display text on Wemos Mini OLED display
+fl ${CBP}/lib/fb.fth
+fl ${CBP}/lib/font5x7.fth
+fl ${CBP}/lib/ssd1306.fth
+
+\ The OLED display is optional, so if it fails to initialize
+\ we discard any messages sent to it.
+0 value oled-active?
+: init-oled ( -- ) 1 2 i2c-setup ['] ssd-init catch 0= to oled-active? ;
+: oled-line ( adr len -- )
+ oled-active? if #columns min fb-type fb-cr else 2drop then
+;
+: oled-cr ( -- ) oled-active? if fb-cr then ;
+
+: init-peripherals ( -- )
+ 6 5 0 true init-gpio-rgb-led \ R:D6 G:D5 B:D0 active-high
+ 3 init-gpio-switch \ Switch on D3
+ init-oled
+;
+
+\ Send a character on UART1. The commonly-used ESP12F modules can
+\ only transmit, not receive, on UART1, because the die pins for
+\ UART1 Rx are not connected to any external pins on the module.
+\ We receive on UART0 Rx via the D7 pin, using system_uart_swap
+\ to move the Rx line from its usual pin. It is tempting to transmit
+\ on the swapped D8 pin, but the system fails to boot if GRBL's Rx line
+\ is connected to D8 when you first power on the system. Fortunately,
+\ D4 doesn't have that booting problem, so we can
+
+: tx1 ( c -- )
+ \ Wait for space in the FIFO. At the usual 115200 baud, this should
+ \ take no longer than about 100 usec.
+ begin $6000.0f1c l@ #16 rshift $ff and $126 >= while relax repeat
+ \ Write the character to the FIFO
+ $6000.0f00 l!
+;
+
+\ Move the UART pins around so we can talk to GRBL instead of to
+\ the normal Rx/Tx pins connected to the USB serial chip.
+: grbl-setup ( -- )
+ 1 2 << $6000.0308 l! \ Disable GPIO2 on D4 pin
+ $20 $6000.0838 l! \ Set D4 (ESP GPIO2) pin to UART1 TX
+ system_uart_swap #10 ms \ KEY listens on D7
+;
+\ Restore UART0 to the normal Rx/Tx pins, so we can debug if necessary
+: grbl-teardown ( -- )
+ system_uart_de_swap \ Restore KEY to normal RX pin
+ #out off
+;
+: grbl-write ( adr len -- ) bounds ?do i c@ tx1 loop ;
+
+
+#1000 constant ticks/ms
+0 value time-limit
+: set-timeout ( #ms -- ) ticks/ms * timer@ + to time-limit ;
+: timeout? ( -- flag ) timer@ time-limit - 0>= ;
+
+0 value time-ms
+: +time-limit ( -- ) time-ms set-timeout ;
+
+
+\ Depends on system_uart_swap having been previously run so
+\ that key? and key, which use UART0, read GRBLs Tx via D7
+: grbl-read-timed ( adr len ms2 ms1 -- -1 | #read )
+ set-timeout to time-ms ( adr len )
+ tuck 0 ?do ( len adr )
+ begin key? 0= while ( len adr )
+ 1 ms ( len adr )
+ timeout? if ( len adr )
+ 2drop i unloop exit ( -- #read )
+ then ( len adr )
+ repeat
+ key over i + c! ( len adr )
+ +time-limit ( len adr )
+ loop ( len adr )
+ drop ( #read )
+;
+
+\ This last-was-status? dance lets us display GRBL coordinate status
+\ on a single display line, then switch to a new line when something
+\ else needs to be displayed.
+false value last-was-status?
+: ?add-cr ( -- )
+ last-was-status? if
+ oled-cr
+ false to last-was-status?
+ then
+;
+: ?oled-pad-line ( n -- ) ?dup if pad swap oled-line then ;
+: wait-grbl-quiet ( -- ) pad #100 #10 #100 grbl-read-timed ?oled-pad-line ;
+
+\ Unused; we don't have a connection to the GRBL reset line, so we
+\ cannot force a reset except by power cycling. GRBL will have
+\ already issued its signon message by the time we can configure
+\ the serial port to see it.
+: wait-grbl-signon ( -- len ) pad #100 #200 #3000 grbl-read-timed ;
+
+#256 constant /response
+/response buffer: response-buf
+0 value #response
+: response$ ( -- adr len ) response-buf #response ;
+
+\ Entry/exit condition: response-buf does not contain a complete line
+\ Action: Add any available Rx characters to the buffer. Remove
+\ and handle any complete lines that result
+
+: remove-line ( index -- )
+ response$ rot /string ( tail$ )
+ dup to #response ( tail$ )
+ response-buf swap move ( )
+;
+
+\ The GRBL buffer is in the controller. We track its free space.
+\ When #grbl-avail is more than the next gcode line length, we
+\ send that line.
+#127 constant /grbl-buf \ Total size of GRBL input buffer
+/grbl-buf value #grbl-avail \ Free space in GRBL input buffer
+
+\ The local gcode buffer holds lines that have already been
+\ sent to GRBL, plus one line waiting to be sent.
+\ As acks ("ok") arrive from GRBL, the oldest, i.e. the first,
+\ line is removed from the local buffer.
+
+/grbl-buf 2* constant /gcode-buf
+/gcode-buf buffer: gcode-buf
+0 value #gcode \ Occupied space in our gcode buffer
+0 value /next-gcode-line \ Length of line waiting to be sent
+: gcode-buf-avail ( -- n ) /gcode-buf #gcode - ;
+: gcode-buf-end ( -- adr ) gcode-buf #gcode + ;
+: 'next-gcode-line ( -- n ) gcode-buf-end /next-gcode-line - ;
+
+\needs cindex fl lex.fth
+
+\ The oldest GCode line is the first one in the buffer.
+\ It is the first one that GRBL has not yet acknowledged.
+: oldest-gcode-linelen ( -- n )
+ #gcode 0= if 0 exit then
+ gcode-buf #gcode carret cindex ( false | adr true )
+ dup 0= if grbl-teardown then
+ 0= abort" Error: missing newline in GCode buffer" ( adr )
+ gcode-buf - 1+ ( n )
+;
+
+\ When a GRBL ACK ("ok") is received, we remove the oldest
+\ line in our buffer and update #grbl-avail to reflect
+\ the amount of space now available in the GRBL input buffer.
+: remove-gcode-line ( -- )
+ oldest-gcode-linelen ( len )
+ dup #grbl-avail + to #grbl-avail ( len )
+ #grbl-avail /grbl-buf > if ( len )
+ grbl-teardown
+ ." GRBL buf underflow"
+ debug-me
+ then ( len )
+ ?add-cr gcode-buf over 1- oled-line ( len )
+ #gcode over - to #gcode ( len )
+ gcode-buf + gcode-buf #gcode move ( )
+;
+
+false value gcode-eof-reached?
+: gcode-sent? ( -- flag )
+ #gcode 0= gcode-eof-reached? and
+;
+
+0 value gcode-fid
+/grbl-buf constant max-gcode-line
+
+\ Move a GCode line from the input file to the transmit buffer
+\ It will be sent when GRBL has room in its input buffer
+: read-gcode-line ( -- )
+ gcode-eof-reached? if exit then
+ gcode-buf-end /grbl-buf gcode-fid ( adr len fid )
+ read-line if #-98 throw then ( cnt more? )
+ dup 0= to gcode-eof-reached? ( cnt more? )
+ if ( cnt )
+ 1+ to /next-gcode-line ( )
+ #gcode /next-gcode-line + to #gcode ( )
+ carret gcode-buf-end 1- c! ( )
+ else ( cnt )
+ drop ( )
+ then ( )
+;
+: ?read-gcode-line ( -- )
+ /next-gcode-line 0= if read-gcode-line then
+;
+
+1 [if]
+: request-grbl-status ( -- ) ;
+: show-status ( adr len -- ) 2drop ;
+[else]
+0 value #grbl-lines
+: show-send-status ( -- )
+ ." #grbl-avail " #grbl-avail .
+ ." #gc " #gcode .
+ ." /gc " /next-gcode-line .
+ cr
+;
+: request-grbl-status ( -- ) " ?" grbl-write ;
+: show-status ( adr len -- ) 1- type (cr ;
+[then]
+
+\ If there is a GCode line waiting to be sent and
+\ GRBL has room, send it.
+: ?send-grbl-line ( -- )
+ /next-gcode-line 0= if exit then
+ /next-gcode-line #grbl-avail <= if
+ 'next-gcode-line /next-gcode-line grbl-write
+ #grbl-avail /next-gcode-line - to #grbl-avail
+ 0 to /next-gcode-line
+ then
+;
+
+\ Handlers for the various GRBL responses
+: handle-ok ( -- )
+ 2drop remove-gcode-line
+ \ Update transmit buffer stats
+;
+: handle-status ( adr len -- )
+ show-status
+ true to last-was-status?
+;
+: handle-error ( adr len -- )
+ oled-line
+ remove-gcode-line
+;
+: handle-other ( adr len -- )
+ ?add-cr oled-line
+;
+
+: $starts-with? ( $1 $2 -- flag )
+ 2 pick over < if 2drop 2drop false exit then
+ rot drop tuck $=
+;
+: handle-grbl-response ( adr len -- )
+ dup 0= if 2drop exit then ( adr len )
+ 2dup " ok" $= if ( adr len )
+ handle-ok exit
+ then
+ 2dup " error" $starts-with? if ( adr len )
+ handle-error exit
+ then
+ over c@ '<' = if ( adr len )
+ handle-status exit
+ then
+ handle-other
+;
+
+\ Read any available response characters from GRBL into our buffer.
+\ When complete lines are present in the buffer, process and remove them.
+: get-response ( -- )
+ response-buf /response #response /string ( adr len )
+ #10 #200 grbl-read-timed ( #read )
+ dup 0<= if drop request-grbl-status exit then ( #read )
+ #response + to #response ( )
+ begin response$ linefeed cindex while ( lf-adr )
+ response-buf - ( lf-offset )
+ response-buf over 1- handle-grbl-response ( lf-offset ) \ Omit CR with 1-
+ 1+ remove-line ( ) \ Omit LF with 1+
+ repeat ( )
+;
+
+\ Ensure that GRBL is responsive
+: sync-grbl ( -- error? )
+ " "r" grbl-write
+ \ GRBL should respond quickly to the carriage return by
+ \ sending an "ok". We read everything that comes back.
+ \ It's an error if we get nothing.
+ pad #200 #50 #1000 grbl-read-timed 0=
+;
+
+: $send-gcode-file ( filename$ -- error? status$ )
+ r/o open-file if drop true " No gcode" exit then ( fid )
+ sync-grbl if true " no GRBL" exit then
+ to gcode-fid ( )
+ false to gcode-eof-reached?
+ 0 to #gcode
+ /grbl-buf to #grbl-avail
+ begin gcode-sent? 0= while
+ get-response
+ ?send-grbl-line
+ ?read-gcode-line
+ repeat
+ gcode-fid close-file drop
+ false " DONE"
+;
+: send-the-file ( -- )
+ grbl-setup
+ " gcode" $send-gcode-file
+ grbl-teardown
+;
+
+\ UI based on a switch, a multicolor LED, and optionally a small text display
+\ When the LED is yellow it is ready to go. Press the switch to start and
+\ the LED turns green while sending. If all goes well, the LED turns yellow
+\ again to start over.
+\ If an error occurs the LED turns red and you have to press/release the switch
+\ to acknowledge the problem and get back to yellow.
+: run ( -- )
+ init-peripherals
+ begin
+ yellow-led " READY" oled-line
+ wait-switch-pressed
+ green-led " SENDING" oled-line
+ wait-switch-released
+ send-the-file oled-line if
+ red-led wait-switch-pressed
+ magenta-led wait-switch-released
+ then
+ again
+;