ESP32forth

Download

ESP32forth.zip - Single .ino file ready for installation
Version: 7.0.3 Rev: e39ea2dc53d89c23673b33935510d9fae987ba3e

http://github.com/flagxor/eforth - Complete Source Code (under ueforth/)

Install

Download the Arduino IDE for your platform.

Go to File > Preferences.
Under Additional Board Manager URLs enter: https://dl.espressif.com/dl/package_esp32_index.json

Choose these options under Tools.

Use

Initially ESP32forth can be interacted with over a serial port (over USB). You can do this from the Arduino IDE's Serial Monitor option. Be sure to config the serial port to: baud rate = 115200, data bits = 8, stop bits = 1, and parity = N.

On boot, ESP32forth configures PIN 2 (typically an LED) to be an output and brings it HIGH (lighting the LED after boot).

ESP32forth Features

ESP32forth Specific Words

Null Terminated Strings

As null terminated strings are used throughout C interfaces, their use is supported in Forth by way of several non-standard words with the convention of using Z/z to refer to such strings in names and stack comments.

Z" ( "string" -- z ) Creates a null terminated string on the heap
Z>S ( z -- a n ) Convert a null terminated string to a counted string
S>Z ( a n -- z ) Conver a counted string string to null terminated (copies string to heap)
Raw Strings

Raw strings are provided better support using a string for the duration of the current command, without consuming heap memory.

R" ( "string" -- a n ) Creates a temporary counted string
R| ( string| -- a n ) Creates a temporary counted string ending with |
Utilities
DUMP ( a n -- ) Dump a memory region
SEE ( "name" -- ) Attempt to decompile a word
VARIABLE ECHO -- Determines if commands are echoed
Vocabularies

ESP32forth uses a hybrid of Forth-79 and Forth-83 style vocabularies. By default vocabularies chain to the vocabulary in which they were defined, as in Forth-79. However, like Forth-83, ALSO can be used to add vocabularies to a vocabulary stack of which CONTEXT @ is the first item. The word ONLY clears the vocabulary stack, but as there is no separate ONLY vocabulary, it also sets CONTEXT to the FORTH vocabulary. The word SEALED modifies the most recently defined vocabulary such that it does not chain. Note, this must be done before words are added to it.

VOCABULARY ( "name" ) Create a vocabulary with the current vocabulary as parent
FORTH ( -- ) Make the FORTH vocabulary the context vocabulary
DEFINITIONS ( -- ) Make the context vocabulary the current vocabulary
VLIST ( -- ) List the words in the context vocabulary (not chains)
WORDS ( -- ) List the words in the context vocabulary (including chains)
TRANSFER ( "name" ) Move a word from its current dictionary to the current vocabulary
                    Useful for "hiding" built-in words
TRANSFER{ ..words.. }TRANSFER ( -- ) Transfer multiple words to the current vocabulary
ALSO ( -- ) Duplicate the vocabulary at the top of the vocabulary stack
ONLY ( -- ) Reset context stack to one item, the FORTH dictionary
            Non-standard, as there's no distinct ONLY vocabulary
ORDER ( -- ) Print the vocabulary search order
SEALED ( -- ) Alter the last vocabulary defined so it doesn't chain
Interpret Time Conditions

[I]F, [ELSE], and [THEN] can be used to selectively compile. Used in tandem with DEFINED? they can be used to handle the absence of modules gracefully. Nesting is supported.

DEFINED? ( "name" -- xt|0 ) Check if a word exists (works at compile time too).
[IF] ( f -- ) Conditionally interpret the text the follows.
[ELSE] ( -- ) Interpret time ELSE.
[THEN] ( -- ) Interpret time THEN.
Blocks
USE ( "name" -- ) Use "name" as the blockfile, e.g. USE /spiffs/foo
OPEN-BLOCKS ( a n -- ) Open a file as the block file
LOAD ( n -- ) Evaluate a block
THRU ( a b -- ) Load blocks a thru b
LIST ( n -- ) List a block
BLOCK ( n -- a ) Get a 1024 byte block
BUFFER ( n -- a ) Get a 1024 byte block without regard to old contents
UPDATE ( -- ) Mark the last block modified
FLUSH ( -- ) Save and empty all buffers
EMPTY-BUFFERS ( -- ) Empty all buffers
SAVE-BUFFERS ( -- ) Save all buffers
SCR ( -- a ) Pointer to last listed block
Block Editor
These words are available inside the EDITOR vocabulary. Note the block editor places newlines in the 63rd column of each line to make the block file readable in a text editor.
WIPE ( -- ) Blank out the current block
L ( -- ) List the current block
D ( n -- ) Delete a line in the current block
E ( n -- ) Clear a line in the current block
R ( n "text" -- ) Replace a line in the current block
A ( n "text" -- ) Add (insert) a line in the current block
P ( -- ) Move to the previous block
N ( -- ) Move to the next block
Utilities
SEE ( "name" -- ) Attempt to decompile a word
ECHO ( -- a ) -- Address of flag that determines if commands are echoed

ESP32forth Bindings

Because Arduino builds a statically linked image for flashing into ESP32 devices, all C function bindings need to be explicitly added. This is the current collection. Typically to reduce confusion, function names have be preserved even through verbose. In popular cases a shorted higher level name is provided.

See template.ino.

Allocation
These words are inside the internals vocabulary.
MALLOC ( n -- a | 0 )   System malloc
SYSFREE ( a -- )   System free
REALLOC ( a n -- a | 0 )   System realloc
Serial
These words are inside the Serial vocabulary.
Serial.begin ( baud -- )   Start serial port
Serial.end ( -- )   End serial port
Serial.available ( -- f )   Is serial data available
Serial.readBytes ( a n -- n )   Read serial bytes, return number gotten
Serial.write ( a n -- n )   Write serial bytes
Serial.flush ( -- )   Flush serial buffer
Serial Bluetooth
These words are inside the bluetooth vocabulary.
SerialBT.new ( -- bt )   Allocate new BT object
SerialBT.delete ( bt -- )   Free BT object
SerialBT.begin ( localname ismaster bt -- f )
SerialBT.end ( bt -- )
SerialBT.available ( bt -- f )
SerialBT.readBytes ( a n bt -- n )
SerialBT.write ( a n bt -- n )
SerialBT.flush ( bt -- )
SerialBT.hasClient ( bt -- f )
SerialBT.enableSSP ( bt -- )
SerialBT.setPin ( z bt -- f )
SerialBT.unpairDevice ( addr bt -- f )
SerialBT.connect ( remotename bt -- f )
SerialBT.connectAddr ( addr bt -- f )
SerialBT.disconnect ( bt -- f )
SerialBT.connected ( timeout bt -- f )
SerialBT.isReady ( checkMaster timeout -- f )   Default checkMaster=false, timeout=0
Bluetooth
These words are inside the bluetooth vocabulary.
esp_bt_dev_get_address ( -- a ) addr of 6 byte mac address
GPIO
pinMode ( pin mode -- )   Set GPIO pin mode
digitalWrite ( pin value -- )   Set GPIO pin state
analogRead ( pin -- n )   Analog read from 0-4095
pulseIn ( pin value usec -- usec/0 )  Wait for a pulse
dacWrite ( pin 0-255 -- )  Write to DAC (pin 25, 26)
ledc
These words are inside the ledc vocabulary.
ledcSetup ( channel freq resolution -- freq )
ledcAttachPin ( pin channel -- )
ledcDetachPin ( pin -- )
ledcRead ( channel -- n )
ledcReadFreq ( channel -- freq )   Get frequency (x 1,000,000)
ledcWrite ( channel duty -- )
ledcWriteTone ( channel freq )   Write tone frequency (x 1000)
ledcWriteNote ( channel note octave -- freq )
Short GPIO Names
pin ( value pin# -- )   Set GPIO pin value
adc ( pin# -- n )   Analog read pin, result 0-1023
System
MS ( n -- )
TERMINATE ( n -- ) Call system exit
Files
R/O ( -- mode )
R/W ( -- mode )
W/O ( -- mode )
BIN ( mode -- mode )
CLOSE-FILE ( fh -- ior )
OPEN-FILE ( a n mode -- fh ior )
CREATE-FILE ( a n mode -- fh ior )
DELETE-FILE ( a n -- ior )
WRITE-FILE ( a n fh -- ior )
READ-FILE ( a n fh -- n ior )
FILE-POSITION ( fh -- n ior )
REPOSITION-FILE ( n fh -- ior )
FILE-SIZE ( fh -- n ior )
WiFi
These words are inside the WiFi vocabulary.
WiFi.config ( ip dns gateway subnet -- )   Packaged a.b.c.d little-endian
Wifi.begin ( ssid-z password-z -- )
Wifi.disconnect ( -- )
WiFi.status ( -- n )
WiFi.macAddress ( a -- )
WiFi.localIP ( -- ip )
WiFi.mode ( mode -- ) WIFI_MODE_NULL WIFI_MODE_STA WIFI_MODE_AP WIFI_MODE_APSTA
WiFi.setTxPower ( powerx4 -- )   Set power x4
WiFi.getTxPower ( -- powerx4 )   Get power x4
mDNS
MDNS.begin ( name-z -- )   Start multicast dns
SPIFFS
These words are inside the SPIFFS vocabulary.
SPIFFS.begin ( format-on-fail path-z max-files -- f )
SPIFFS.end ( -- )
SPIFFS.format ( -- f )
SPIFFS.totalBytes ( -- n )
SPIFFS.usedBytes ( -- n )
WebServer
These words are inside the WebServer vocabulary.
WebServer.new ( port -- ws )   Allocate new webserver object
WebServer.delete ( ws -- )   Delete webserver object
WebServer.begin ( port ws -- )
WebServer.stop ( ws -- )
WebServer.on ( path-z xt ws -- ) Set up a web path handle callback
WebServer.handleClient ( ws -- )   Handle one client request
WebServer.hasArg ( z ws -- f )   By name
WebServer.arg ( z ws -- z )   By name
WebServer.argi ( n ws -- z )   By index
WebServer.argName ( n ws -- z)   By index
WebServer.args ( ws -- n )   Number of args
WebServer.setContentLength ( n ws -- )
WebServer.sendHeader ( name-z value-z fist ws -- )
WebServer.send ( code mimetype data ws -- )
WebServer.sendContent ( z ws -- )
WebServer.method ( ws -- n )   GET / POST etc.
Wire
These words are inside the Wire vocabulary.
Wire.begin ( -- f )
Wire.setPins ( sda scl -- f )
Wire.setClock ( frequency -- )
Wire.getClock ( -- frequency )
Wire.setTimeout ( ms -- ) Default is 50ms
Wire.getTimeout ( -- ms )
Wire.lastError ( -- n )
Wire.getErrorText ( n -- z )
Wire.beginTransmission ( n -- )
Wire.endTransmission ( sendstop -- f ) Default is true
Wire.requestFrom ( address quantity sendstop -- n )
Wire.writeTransmission ( addr a n sendstop -- err )
Wire.readTransmission ( addr a n sendstop acount -- err )
Wire.write ( a n -- n )
Wire.available ( -- f )
Wire.read ( -- ch )
Wire.peek ( -- ch )
Wire.busy ( -- f )
Wire.flush ( -- )
Camera
These words are inside the camera vocabulary.
esp_camera_init ( config -- f )
esp_camera_deinit ( -- f )
esp_camera_fb_get ( -- fb )
esp_camera_fb_return ( fb -- )
esp_camera_sensor_get ( -- sensor )
SD_MMC
These words are inside the SD_MMC vocabulary.
SD_MMC.begin ( mount mode1bit )   default mode1bit=false
SD_MMC.end ( -- )
SD_MMC.cardType ( -- n )
SD_MMC.totalBytes ( -- n )
SD_MMC.usedBytes ( -- n )
Interrupts
These words are inside the INTERRUPTS vocabulary.

High Level words:

pinchange ( xt pin -- ) Call xt when pin changes.

Example:

17 input pinMode
: test ." pinvalue: " 17 digitalRead . cr ;
' test 17 pinchange

Low Level words:

ESP_INTR_FLAG_DEFAULT -- Default handler allows per pin routing

Various triggers:
  GPIO_INTR_DISABLE
  GPIO_INTR_POSEDGE
  GPIO_INTR_NEGEDGE
  GPIO_INTR_ANYEDGE
  GPIO_INTR_LOW_LEVEL
  GPIO_INTR_HIGH_LEVEL

gpio_config ( gpio_config_t* -- 0/err )
gpio_reset_pin ( pin -- 0/err )

gpio_set_intr_type ( pin type -- 0/err )

gpio_intr_enable ( pin -- 0/err )
gpio_intr_disable ( pin -- 0/err )

gpio_set_level ( pin level -- 0/err )
gpio_get_level ( pin -- level )

gpio_set_direction ( pin mode -- 0/err )

gpio_set_pull_mode ( pin mode -- 0/err )

gpio_wakeup_enable ( pin type -- 0/err )
gpio_wakeup_disable ( pin -- 0/err )

gpio_pullup_en ( pin -- 0/err )
gpio_pullup_dis ( pin -- 0/err )
gpio_pulldown_en ( pin -- 0/err )
gpio_pulldown_dis ( pin -- 0/err )
gpio_hold_en ( pin -- 0/err )
gpio_hold_dis ( pin -- 0/err )

gpio_deep_sleep_hold_en ( -- )
gpio_deep_sleep_hold_dis ( -- )

gpio_install_isr_service ( a -- ) Typically ESP_INTR_FLAG_DEFAULT
gpio_uninstall_isr_service

gpio_isr_handler_add ( pin xt arg -- 0/err )
gpio_isr_handler_remove ( pin -- 0/err )

gpio_set_drive_capability ( pin cap -- 0/err )
gpio_get_drive_capability ( pin cap* -- 0/err )

esp_intr_alloc ( source flags xt args handle* -- 0/err )
esp_intr_free ( handle -- 0/err )

timer_isr_register ( group timer xt arg ret -- 0/err )
Timers
These words are inside the TIMERS vocabulary.

High Level words:

( timer t = 0-3 )

interval ( xt usec t -- ) Setup timer t to call xt each after usec
rerun ( t -- ) Rerun timer t triggering

Example:
timers
: hi   ." hi" cr   0 rerun ;
' hi 1000000 0 interval ( run hi every second )

Medium Level words:

timer@ ( t -- lo hi )
timer! ( lo hi t -- )
alarm ( t -- a )
enable! ( f t -- ) Timer enable/disable
increase! ( f t -- ) Timer increasing/decreasing
divider! ( n t -- ) Timer divider 2 - 65535
edgeint! ( f t -- ) Edge trigger
levelint! ( f t -- ) Level trigger
alarm-enable! ( f t -- ) Alarm enable
alarm-enable@ ( t -- f ) Alarm enabled?
onalarm ( xt t -- ) Set callback

Low Level words:

( group n = 0/1, timer x = 0/1, watchdog m = 0-5 )

TIMGn_TxCONFIG_REG ( n x -- a )
TIMGn_TxLOHI_REG ( n x -- a )
TIMGn_TxUPDATE_REG ( n x -- a )
TIMGn_TxALARMLOHI_REG ( n x -- a )
TIMGn_TxLOADLOHI_REG ( n x -- a )
TIMGn_TxLOAD_REG ( n x -- a )

TIMGn_Tx_WDTCONFIGm_REG ( n m -- a )
TIMGn_Tx_WDTFEED_REG ( n -- a )
TIMGn_Tx_WDTWPROTECT_REG ( n -- a )

TIMGn_RTCCALICFG_REG ( n -- a )
TIMGn_RTCCALICFG1_REG ( n -- a )

TIMGn_Tx_INT_ENA_REG ( n -- a )
TIMGn_Tx_INT_RAW_REG ( n -- a )
TIMGn_Tx_INT_ST_REG ( n -- a )
TIMGn_Tx_INT_CLR_REG ( n -- a )
RTOS
These words are inside the RTOS vocabulary.
xPortGetCoreID ( -- n )
xTaskCreatePinnedToCore ( fn name stack-depth params priority taskout coreid -- )
vTaskDelete ( task ) -- ) 

ESP32 WebUI

A terminal over the web can be activated. Contact at port printed or via mDNS http://forth/.

webui ( network-z password-z -- )

Usage:

z" NETWORK-NAME" z" PASSWORD" webui

See web_interface.fs.

Autoexec.fs

The system will automatically attempt to mount SPIFFS filesystem at /spiffs. It will then at start attempt to load /spiffs/autoexec.fs

One way this feature can be used to configure the Web UI to start by default. When doing this, be sure to test your Web UI settings work well first.

r| z" NETWORK-NAME" z" PASSWORD" webui | s" /spiffs/autoexec.fs" dump-file

To remove a previously configured autoexec.fs you will need to be able to reboot in a mode with Forth. One way to do this is to search for the line in the .ino file that refers to autoexec.fs and replace it with a different name. Then run the following:

s" /spiffs/autoexec.fs" delete-file

See autoboot.fs.

Adding Words

Adding words based on C functions can be done by editing the source code. For ESP32forth template.ino is the appropriate place.

Because of the use of X-Macros words can be added in as little as one line. Locate the macro called PLATFORM_OPCODE_LIST and add your words there.

NOTE: Be careful to end each line with a \ so the macros will chain correctly.

To add a word containing only letters, numbers, and underscore you can use Y.

  Y(MY_WORD123, c_function_to_call()) \

If your word name contains other characters, instead use X. You will need to make up an alternate name for the middle parameter. It must contain only letters, numbers, and underscore. The name is not used anywhere else, but must be unique.

  X("myword!", MY_WORD_BANG, c_function_to_call()) \

Values from the data stack can be accessed via the variables tos (Top of Stack) and sp (Pointer to the rest of the stack). The stack has the data type cell_t.

You can push a value of any type to the stack with the macro PUSH:

  Y(MY_WORD, PUSH calculate_magic_value()) \

To simplify calling C functions, you can also refer to elements on the stack with the types positional names:

n10 n9 n8 n7 n6 n5 n4 n3 n2 n1 n0 - Access stack as cell_t integer values
                   c4 c3 c2 c1 c0 - Access stack as char* values
                   b4 b3 b2 b1 b0 - Access stack as uint8_t* byte values
             a6 a5 a4 a3 a2 a1 a0 - Access stack as void* values

Examples:
  void send_message(const char *message, int code);
  ...
  X("send-message", SEND_MESSAGE, send_message(c1, n0)) \

You can always replace the top item on the stack with SET:

  Y(DECODE, SET decode_func(n0)) \

You can drop elements from the stack with DROP or DROPn(number). You can nip elements from below top of the stack with NIP or NIPn(number). Be aware that like PUSH this will cause stack indices to change.

  int my_cool_function(char *foo, int bar, uint8_t *baz);
  ...
  Y(MY_FUNC, n0 = my_cool_function(c2, n1, b0); NIPn(2)) \

Multiple C statements can be included in the code area of a word, but care must be taken to generally avoid {}s. If you find you need nesting, a separate function is recommended.

New variables can be declared in each word and are scoped to that word.

  Y(MY_WORD, cell_t foo = n0; DROP; char *x = c0; DROP; \
             PUSH my_func(foo, x)) \