eForth for cheap STM8S Value Line gadgets

Turn cheap stuff from AliExpress into interactive development kits!

Similar projects worth following
This project turns "Value Line" STM8S µC boards into Forth development kits. The code is based on Dr. C.H. Ting's STM8EF eForth demo for the STM8S Discovery, an interactive Forth that crams kernel, interpreter and compiler into 5.5K Flash. For STM8 Value Line µCs with 8K Flash that was still too much, but after a lot of refactoring the demo fits in just 3.8K!

Some of the free Flash memory was used for adding new features, e.g. compile to Flash, interrupt handlers, board drivers (for 7S-LED displays, analog and digital I/O), a background task, vectored I/O, or enhanced vocabulary (e.g. DO..LOOP, CREATE..DOES>). A simple configuration framework provides feature selection, and support for new boards. The SDCC tool chain allows mixing C with Forth, e.g. as a shell for testing, setting parameters, or scripting.

Check out the docs in the Wiki, get cheap hardware, and enjoy interactive µC hacking!

What is it good for?

The project delivers configurable board support code for selected targets, and docs. Besides its modest size, the TG9541/STM8EF code has a long feature list. Using the code for embedded control applications is subject to new projects.

The code on GitHub can be used in many ways:

  • for writing alternative firmware Chinese commodity boards (e.g. thermostats, DCDC converters, or relay boards)
  • for embedded systems with an interactive shell (scriptable and extensible)
  • for creating smart SPI, I2C, or RS232 smart sensors with a scripting shell, e.g. for RaspberryPi, Arduino, or ESP8266
  • as an interactive environment for exploring the STM8 architecture
  • for learning Forth. It's easy and fun - find out why in the text below!
  • ...

Why a Forth for Cheap Chinese boards?

Because it's fun: cheap mass-produced imperfection is a playground for creativity :-)

Right now, the W1209 is my favorite target: it's a rather complete embedded control board with a UI at a very good price. It's as cheap as it looks, and the challenge is in it's imperfections: the guy who designed the board clearly didn't have a connected application in mind, and I had a lot of fun making it do things it was never intended to do.

There are challenges, like the lack of communication ports. The "sensor connector" can either be used for communicating, or for sensing. What if you need sensing and communication at the same time? Maybe the "update connector" can be used as a home brew field bus interface? A lot is possible with the right idea, and the right software!

Which target boards are supported?

Besides generic CORE target for STM8S003F3P6, there is currently support for the following boards:

I also ordered the following SmartClima control boards for tests:

@Elliot Williams worked on using the ESP-14 as an IoT deviced (the ESP-14 is an ESP8266 with an STM8S003F3P6 in a ESP-12 package).

Programmable power supplies based on the XH-M188, and a cheap DC/DC converter are work in progress. There are also several types of STM8S003F3 based voltmeters that can be supported.

Read more about likely future targets below.

Why Forth?

Again, because it's fun!

Consider this:

  • compared to other programming environments the core of Forth is easy to fully understand
  • like Lisp, Forth has a REPL (Read-Evaluate-Print-Loop) which enables software testing in a way impossible with "Edit-Compile-Run-Debug" (e.g. Arduino)
  • it's easy to build Domain Specific Languages (you can literally program the compiler!)
  • the stack-centered "factoring" approach provides implicit data flow which leads to maintainable code
  • Interpreter-compiler, basic OS functions fit in just 4K code :-)

Forth starts out as a stack machine with a tiny instruction set and minimal hardware requirements. It fits in a few KiB, and the target, even a lowly µC, can even be used as the development system. The Forth stack machine is a minimalistic VM on a standard CPU, but there are also hardware implementations (e.g. for FPGAs, or a 144 core Forth processor). The VM is ideal for fast context switching and Forth easily meets hard-real-time requirements. It's no surprise that Forth was used in many NASA projects.

A Forth programmer is in control of all levels of problem abstraction, a unique advantage in a world where layer on layer of 2nd hand solutions leads to ever growing complexity (compilers, libraries, operating systems, drivers,frameworks, IDEs... ). I'm convinced that "Thinking Forth" will make anybody a better programmer, not just in the domain of embedded control!

Why STM8S003F3 or STM8S103F3?


Read more »

The latest release is on GitHub. Release v2.2.0

Zip Archive - 18.94 kB - 12/04/2016 at 23:22


Original version of STM8EFalong with docs as received from Dr. C.H. Ting on 21/Nov/2016. The docs are worth reading, the eForth binary will run on the STM8S Discovery.

Zip Archive - 21.97 kB - 11/21/2016 at 20:13


View all 2 files

  • 1 × ST-Link V2 ICP adapter (e.g. $2.00 from AliExpress) The ST-Link on an STM8S Discovery Board can be used, too
  • 1 × serial interface /w 3.3V level (e.g. a $0.60 CH340 USB adapter) e.g a CH340 USB interface
  • 1 × STM8S target device as listed in the GitHub Wiki (e.g. a $0.70 "STM8S103F3P6 minimal system board") e.g. "STM8S103F3P6 STM8S development board" from your favorite China source
  • 1 × Some headers, patchwires, breadboard etc (about $2.00)

  • W1401: board support "complete" (update)

    Thomas3 days ago 0 comments

    The W1401 thermostat board has now initial STM8EF board support, and it's also better documented. There are maybe still some rough edges, but the things I tested work "as expected". It's now part of the "official" release v2.2.7.

    The following features are supported:
    • serial interface through PD1/SWIM
    • 7S-LED through vectored I/O (auto-off w/ time delay while serial interface active)
    • board keys through vectored I/O
    • relay/red LED, and green LED through `OUT!`
    • buzzer with programmable pitch (`BEEP_CSR` HW-register)
    • read sensor input with `5 ADC! ADC@`

    The analog input is surprisingly stable: there is just enough noise to get some extra resolution, but one still gets a stable readout down to the LSB without filtering!

    The programmable pitch of the BEEP output isn't good enough for playing music (unless you spend extra effort to tuning the RC-oscillator, or if you don't mind annoying people).

    The price for a W1401 starts from $2.60/piece (including shipping). It offers a lot of value for the money, especially if you're looking for a hackable board for SISO (Single Input Single Output) control (e.g. thermostat, solar heating, level control, charge control), or for monitoring/safeguarding applications!

  • STM8EF on the ESP-14: Hacking on the Weirdest ESP Module!

    Thomas4 days ago 0 comments

      While I was on a business trip something left a spike on the visitors graph on GitHub: @Elliot Williams wrote about his ESP-14 with MQTT-and-STM8EF project on Hackaday!

      ESP-14 is an ESP8266 with an attached STM8003F3P6 in a package very similar to an ESP-12 module. Elliot used the ESP-14 for two reasons:

      1. the ESP-14 has more ADC inputs than the ESP-12
      2. a powerful ESP8266 bundled with a minimal STM8S running Forth is a crazy thing waiting to be done!

      The schematics around the ESP-14 looks like this:

      The ESP8266 connects the tiny STM8S003F3P6 to a Linux machine using JeeLab’s esp-link.

      The ASCIIcinema recording of a remote log-in via WiFi to an 8bit Forth, with a demo of interactive testing on the Forth console is well worth watching!

      Check out Elliot's article, it's great!

      PS: Elliot and me discussed other use cases for the ESP-14, like battery powered IoT devices. All the tools to make this happen are now available!

  • Boards and Docs

    Thomas02/11/2017 at 10:42 0 comments

    The core code in this project has reached some degree of maturity, and I guess it's the right time to shift the attention to board support code, and to docs.

    The following needs to be done:

    • publish contents from notebooks, and also from this log, to the Wiki on GitHub
    • restructure the Wiki without breaking links on external pages
    • use custom GitHub sidebars and overview pages to make the contents accessible
    • Enrich Wiki pages with diagrams, schematics, and photos
    • Create a simple HowTo with a shopping list, and instructions that require "zero-problem-solving".

    There is a page at GitHub that presents "Best Practice" for GitHub Wikis, but "Best" seems to stand for "use it for more than leaving an outdated boring Markdown wasteland", and "Practice" appears to mean "Improvise".

    Documentation in a uniform and manageable way calls for a CMS, but a good folder structure and templates will do the job. I like the method used in this Wiki: it's based on sub-folders with local sidebars and locally stored images. It also shows that it takes quite some discipline to make it effective.

    EDIT: I'm on a business trip, so no hacking :-{ Some of the docs on GitHub received an update.
    There are some more boards in the pipeline. XH-W1401 is half-ready, and I can start with XH-W1219 or XH-1701. In order to improve accessibility to information it's maybe better to create some more HaD sub-projects.

    My idea is:

    • simple control boards (e.g. thermostats),
    • voltmeters
    • power supplies
    • other boards here (unless they require more work)

  • Yet Another Voltmeter

    Thomas02/07/2017 at 22:44 7 comments

    After a day filled with business meetings | didn't want to risk killing the DC/DC converter for good, and I decided to look a little closer at one of the new arrivals from China, small and cheap voltmeter ($1.12 including shipping).

    Repetitio non placet they say, but this voltmeter turned out to be really nice:

    The LED display 3361BS is "0.36", red, common anode". The internal supply voltage is a bit unusual (3.0V), but otherwise the module is very hackable.

    The 6 pads with 1.27mm pitch to the left are connected to the following signals:

    1 SWIM (7S-LED segment D)
    2 TxD
    3 RxD
    4 NRST
    5 +Ub
    6 GND
    The STM8S003F3P6 pins are assigned as follows:
    STM8S003F3P6assigned toSTM8S003F3P6assigned to
    1 PD47S-6 (NC!)20 PD37S-5 G
    2 PD5Pad2 TxD19 PD27S-4 C
    3 PD6Pad3 RxD18 PD1/SWIM7S-3 DP, Pad1 SWIM
    4 NRSTPad4 NRST17 PC77S-2 D
    5 PA1
    7S-9 Digit 216 PC67S-1 E
    6 PA2(7S-9 Digit 2)15 PC57S-7 B
    7 VssGND14 PC4/Ain2TP (535k-Ain-8k26-GND)
    8 VcapC13 PC37S-12 Digit 1
    9 Vdd+3.0V12 PB47S-10 F
    10 PA37S-8 Digit 311 PB57S-11 A

    I guess that the designer was bored by making yet another cheap Chinese voltmeter, and he decided to make a hackable voltmeter instead. If not, why would one expose not only SWIM/NRST but also RxD and TxD? Why route a spare GPIO to the (unpopulated) NC pad of the LED display?

    Hat-tip to China!

  • First STM8EF application project on HaD

    Thomas01/30/2017 at 10:29 0 comments

    In this project log I wrote several times about cheap Chinese DC/DC converters with a STM8S based voltmeter that can be converted into something much more interesting: a programmable power supply.

    Yesterday I started a "HaD Downstream Project" to track the progress with this board. The new project isn't fully independent, as it will contribute requirements to this project (downstream is a bit misleading - in a development process it's actually one integration level up).

    The first requirements are:

    • all aspects of LED_MUX should be handled by the board support code
    • assigning RAM to board support code should not not require changing forth.asm

    The second requirement is trivial to solve.

    I solved the second requirement with a "RAM pool" and a set of macros, and now I wonder why it took so long to do such an obvious thing. The disadvantage of automatic RAM allocation is of course that addresses now depend on the configuration (for the application there is no problem since a Forth word can export the address). For debugging a look into the list file is necessary. I guess I sub-conciously re-applied 80s embedded control practice: the RAM address sheet. Meh.

  • Bump to v2.2.6: new features, same binary size, more user code

    Thomas01/28/2017 at 22:49 0 comments

    I just released STM8EF v2.2.6 with some really nice features from the development branch:

    There was also an important bug fix for DO LEAVE LOOP/+LOOP (LEAVE actually works now, and it's possible to use negative increments).

    Thanks to new coding techniques, e.g. TRAP for literals, the binary size still is within the same bounds as before:

    • interactive Forth (the use case of the original STM8EF code) < 3800 bytes,
    • CORE ("compile to Flash", interrupts in Forth) < 4096 bytes
    • MINDEV (DO-LOOP, CREATE-DOES>, board I/O, background tasks) < 5000 bytes
    • W1209 (7S-LED display, COM-simulation, board keys) < 5500 bytes

    The code generator improvements (relative calls, STM8 opcodes for literals, branch and exit) reduce the size of compiled user code by 20-25%. This should be sufficient for 5 to 10 screens of Forth, enough for non-trivial applications.

    Edit: with this release some of my personal goals were reached, and it's some kind of milestone. I took the opportunity to update the project's description and details. Sorry for the update spam :-)

  • Coding Interrupt Handlers in Forth

    Thomas01/26/2017 at 21:29 0 comments

    @Elliot Williams convinced me that writing interrupt handlers in Forth instead of assembly (or C) is a Cool Thing. I had pondered a lot about how to do that with the least amount of overhead but it took a lengthy discussion with Elliot to get it right!

    There was a problem to solve: the STM8 register X is the Data Stack pointer, and it's also needed for implementing certain core words. The assumption that "X is TOS" isn't always justified. There is no way around this, except by blocking interrupts, or by rewriting code so that X always represents a valid stack pointer (with undesired effects like increased code size or longer runtime).

    My "the most simple thing that works" solution assigns a small clean data stack to user-defined interrupts. The data stack is just 8 cells deep, but that should be more than sufficient (please read below why).

    Interrupt handlers in Forth code are now based on the following words:

    • SAVEC to save the context
    • IRET to restore the context and return from the interrupt
    • IVEC! to set an interrupt vector

    Writing an interrupt handler is easy - here is an AWU (Auto Wake Up) handler as an example:

    : IVEC! ( a n - -   ) 2* 2* $800A + ! ;
    : HALT ( -- ) [ $8E C, ] ;
    : awuint SAVEC awu_csr1 c@ IRET ;
    : initawu 38 awu_apr c! 1 awu_tbr c! 16 awu_csr1 c! ;
    ' awuint 1 IVEC!

    The interrupt handler awuint first does a Forth VM context switch with SAVEC. Reading awu_csr1 clears the AWU interrupt flag, and IRET restores the Forth VM context and returns to the interrupted code with IRET. It's not necessary to leave the stack balanced (otherwise DROP would be required). The word IVEC! stores the address of our new hander to interrupt vector 1 (the AWU interrupt in Flash memory). Since IVEC! is only needed at "compile time" it can be compiled to RAM and doesn't need to be part of the Forth image.

    The word HALT encodes the STM8 HALT instruction that shuts down the CPU clock until an interrupt occurs (I first added a word HALT to the Forth core but this user code implementation is just as good). When I run HALT with this code, it returns because of the Auto Wake Up interrupt. Note that I didn't find the time to make sense of the AWU configuration, and I simply took the AWU timing values from this page.

    When writing interrupt handlers in Forth, I would like to recommend the following practice:

    • interrupts should only be used for low-level code, e.g. for using µC peripherals that require interrupts to work
    • be careful about data- and return stack use - 5 levels deep should be plenty!
    • the code should be fast - if in doubt use pin-debugging with a scope or a simple logic analyzer for testing the timing
    • only do the data processing that's absolutely required for meeting your application's timing constraints. The rest should be done in a low-priority task (e.g. background)
    • simplicity is important: don't use any fancy high-level words (e.g, output string formatting shouldn't be used)
    • one should be carefully assess potential side effects of character I/O (if required!)
    • in many cases you'll need the µC manual to understand what your code is doing, and fancy abstractions won't make your code more readable

    Working code with SAVEC and IRET is in the develop branch on GitHub. I'm still working on some details but it will be part of the next release.

  • More fun!

    Thomas01/26/2017 at 06:39 0 comments

    You may have noticed that this project page isn't one-way communication any longer!

    Several people *) now contribute to the progress in the following ways:

    • by writing their own application code and challenging limitations
    • by contributing with requirements, and discussing implementations
    • testing and filing bug reports
    • writing new board support code, or proposing new boards
    • interest for, and encouragement of, all of the above

    Thanks guys, you're making all this more fun! I hope to see a related project, soon :-)

    *) hat tip to @Elliot Williams, @ajlitt, and others

  • STM8EF v2.2.6.Snapshot: embarrassing hotfix (and new features)

    Thomas01/22/2017 at 17:45 0 comments

    A long time ago, I proudly released the "DO .. LEAVE .. LOOP" feature. I should better have spent more time testing, and less time announcing, since LEAVE was all but working. For some reason, my test cases worked, and some change to the code base must have exposed the bug. The new (pre-) release 2.2.6.Snapshot with the bug fix (and some more features) is here.

    I noticed the bug during tests of new features for code size optimization, like a DOLIT implemented with TRAP, and relative addressing of CALL (now, depending on the code, STC can now be as compact as DTC).

    Release of support of new boards (like W1401 and DCDC) will have to wait a little bit longer, just like introducing the W1219, or a voltmeter with very friendly breakouts that I had in the mail last week. At least, the code in the GitHub "develop" branch contains some initial board support for the W1401 (without keys and outputs), and for the DCDC converter (the serial interface isn't fully usable yet).

  • The code densitiy TRAP

    Thomas01/21/2017 at 10:32 0 comments

    Fusing code to improve code density has a high potential of making refactoring very difficult. In this project I took the freedom to fuse core code when I saw a size (sometimes also a speed) advantage, and where I didn't see possible future variation points. In some cases I un-fused code from the original STM8EF source to get desired variation points (e.g. vectored I/O). This was easy since much of the eForth source code is written in Forth, and fusing code is no problem as there are no fundamental changes to the design of eForth.

    So, where is the point? It's a pun. I used the TRAP instruction to improve the code density of Forth user code :-)

    Please let me explain by diving a bit into how the Forth VM, the virtual CPU that drive a Forth systems works. When Forth compiles a "word", it creates a thread of tokens for consecutive execution by the Inner Interpreter, the execution unit of the Forth VM.

    The Inner Interpreter can be implemented in hardware (a Forth CPU), or in software. There are established coding techniques for general purpose CPUs, like the often used Direct Threaded Code (DTC) where tokens are pointers to the so called "Code Field Address" of words.

    Tokens can represent any of the following:

    • other Forth words like DUP, SWAP, or other user words, that get executed in sequence
    • the DOLIT instruction to push an literal integer, following the token, to the Data Stack
    • the EXIT instruction that ends a thread, to return to the calling thread, or to end execution
    • the BRANCH instruction to move the instruction pointer to a different point in the same thread
    • ?BRANCH, DONEXT, or DOLOOP which are similar to BRANCH but conditional (using data on the Data or Return Stack)

    Words like DOLIT or ?BRANCHdiffer from ordinary Forth words in that they manipulate the Inner Interpreter, and don't just work with the stacks of the Forth VM, and therefor the implementation of this class of words dependents on the coding technique used for implementing the Forth VM. Only a small number of such words is necessary, and most of them are core words.

    STM8EF uses Subroutine Threaded Code (STC). Here, the Inner Interpreter is implemented by the CALL instruction of the CPU, the instruction pointer is the program counter, and the CPU's return stack is used as the Return Stack of the Forth VM. Words that need to manipulate the instruction pointer, like DOLIT, do this by changing the CPU's PC value on the return stack (the one that the CALL pushes for use by RET).

    The advantage of STC is speed and simplicity of implementation. Context switching for multi-tasking is also easy to achieve (I took advantage of this). The disadvantage is code size: compared to DTC a simple STC implementation needs at least one more byte for representing a token, the CALL instruction. And this is before we even use words like DOLIT and EXIT to make the Inner Interpreter change the sequence of execution.

    Since we're using the CPU as the Inner Interpreter there are other some more analogies between the CPU and the Forth VM that we can exploit for optimization:

    Token Optimization measure for increasing code density Trade-Off Done
    any Many CPUs have CALL instruction that uses some form of relative or segmented addressing for improving code density, and an optimizing compiler can take advantage. The STM8S CALLR instruction is used if the call distance is less than 128 bytes (2 instead of 3 bytes). Some code generator complexity.
    EXIT EXIT can be coded as native RET (1 instead of 3 bytes) None
    BRANCH Branch can be coded as native JP (3 instead of 5 bytes) None
    ?BRANCH That's very hard since manipulation of the Data Stack is required (DONEXT and DOLOOP even manipulate data on the Forth VM Return Stack ). In Machine Forth, Chuck Moore changed the rules of the game to make this easier. Be like Chuck Moore.

    Read more »

View all 56 project logs

  • 1

    Get some cheap hardware (e.g. a STM8S103F3P6 breakout board for $0.65 and a ST-Link V2 dongle for $2). download the binary release, flash it, and have fun!

  • 2

    If you like it, and you want to hack board support code for your favorite STM8China gadget, you need:

    • a Linux SDCC tool chain installation: installation instructions for SDCC & stm8flash are in the Wiki
  • 3

    Clone the project on GitHub

View all 4 instructions

Enjoy this project?



Elliot Williams wrote 01/23/2017 at 08:55 point

Hiya Thomas,

Got an ESP-14-powered device up and running and installed in our basement.  Long story, must write up.

Have you played around with power saving modes on the STM8?  I'm trying to get the part into the AWU / active-halt mode.  

For one, I need the assembler's HALT command, which I've been doing in the worst brute-force means possible: HERE $8e81 , EXECUTE.  (That's HALT and RET in machine code.)  

It halts, at least.  :)  

Coming back out of halt is messy -- it looks like the clocks aren't returned to their original states and so on. I'm probably going to need to implement some start-up code.  Heck, for my purposes, hooking into COLD for a complete reset will work too... That's what I'll try next.

Just wondering if you've worked on any of the low-power modes.  Either WFI (wait-for-interrupt) or the active-halt/AWU look tasty.

  Are you sure? yes | no

Thomas wrote 01/23/2017 at 19:14 point

That's great :-)

The power saving modes (like the watchdog) still are on my "important things that I plan to do" list. You know, that's the list on the sheet after "new and exciting things I want to play with", which in turn comes after "bugs I must fix now".

Let's put it on the "important new features for pilot applications" list :-) 

What we need is:

* a word HALT that contains the HALT instructionknow

* a word SLEEP, that stops unnecessary interrupts (user defined, and application specific). This word should run HALT. When the execution continues right after HALT, SLEEP shall re-enable "waking" interrupts

* if required a word to restore clock settings (RM0016 mentions something in 10.2.2 and in 9.9.4 "Clock master switch register (CLK_SWR)", but right now I don't undertsand why the clock changes)

Do you plan to trigger a wake-up through console events? The simulated COM port should support this use case!

  Are you sure? yes | no

Thomas wrote 01/23/2017 at 19:45 point

I added the HALT word, and it works better than expected. Here is a demo with a blinky:

    : g tim 40 and 0= out! ; ok
    ' g bg ! ok
The when I press enter after HALT the LED stops flashing. The "ok" after HALT appears after I press enter a second time.

  Are you sure? yes | no

Elliot Williams wrote 01/24/2017 at 11:52 point


re: clocks: I read something somewhere sometime about them needing a reset.  I can't find that anymore.  I may be crazy.  

I saw some other STM8 code ( that runs the AWU without re-clocking, strongly suggesting that I'm crazy.

That code, though, makes it look like (if interrupts are enabled) the AWU reset lands in the AWU ISR, which is uninitialized ($0000) in the vector table at $800C.

I just ran your BG example above, and it halts, but never returns until hit with a hard reset. I wonder if your code is working b/c it NOPs off to the next ISR and you got lucky.  Or does it actually try to execute whatever's at $0000?

So: how do we set up ISRs in eForth?  (Or, how do you write bytes directly to flash?)

  Are you sure? yes | no

Thomas wrote 01/24/2017 at 19:23 point

TL;DR: the quick-fix: an AWU "driver" that does it all but I would prefer a Forth solution and this requires some design decisions.

Long version:

Due to limitations in the SDCC tool chain any interrupt must be declared in main.c. Writing ISR vectors to Flash might work, but it requires a good approach for registering (and unregistering) interrupts to be viable (I'm thinking of RESET). Also Forth VM context switching would have to be done before executing any Forth code.

Another approach would be a "catchall" interrupt handler for several interrupts that then redirects to Forth code. This has the advantage that the context switch can be handled in a uniform way, but the dispatching won't be very efficient (or again a lookup).

This brings us to the next problem: some interrupt sources require resetting some bit in some peripherals control register. Leaving that to user code is very error prone, and a "catchall" interrupt handler would have to do it for all possible sources or leave it to user code.

What do you feel about of a middle way?
* Interrupt handler declared in main.c
* basic handler code in assembler or c to do a context switch, and to clear the trigger source
* handler code in Forth registered through something like BG

A last point: how many concurrent "Forth code interrupts" can we allow?
* Level0 we have the console
* on Level1 is the BG interrupt
* on Level2 is TIM4 (for COM simulation)

I guess that some stuff like TIM4 shouldn't have to compete with other code (the current code is efficient as it gets). Most likely it's possible to drop the interrupt level in BG code to Level0, and use Level1 for Forth handlers without character-I/O. The latency would still be in the lower µs range.

  Are you sure? yes | no

Elliot Williams wrote 01/25/2017 at 12:43 point

How does the 'BOOT mechanism work?  If you could do placeholders for the various ISRs like that, the user could write their handler function and store its address in the right place?  That seems very Forthy to me.  <code>: awu-isr stuff ; ' awu-isr ISR_AWU ! </code> or something.  One of these functions / memory locations per IRQ and you'd be done?

On resetting the flags as you leave the ISR: I think that should be user code rather than bloating up the system with it.  Yeah, it's going to hang the system if you do it wrong.  If I could count the number of times I've pressed the reset button...

On context switching in ISRs:  I'm not sure I understand the full details.  Unlike C, there's not necessarily any context to switch?  If the ISR maintains stack balance then there's no need for any context?  Leave whatever's on the stack, and it'll still be there when the interrupt is done?

For me, personally, I'd just be stoked to have a pointer to an address that I could set to execute when the AWU IRQ fires.  The rest, I can handle in code, I hope. :)  (Assuming that the return from interrupt works right.)

  Are you sure? yes | no

Thomas wrote 01/25/2017 at 20:20 point

'BOOT is simple: it returns the address of the "Parameter" field (like DOVAR). To safe code I used it to get the address of the whole following table of initialization values for USR variables. After switching to "NVM" it's possible to simply overwrite all these values. There is a 2nd copy to restore these values, e.g. to "forget" user vocabulary in Flash memory with RESET.

Yes, the 'BOOT method can be used in for interrupts, too, but that would require one more level of indirection.

About context switching:

my first approach was to re-use the Data Stack, but I quickly learned that X isn't always a valid Data Stack Pointer: it does that at the start and the end of a word, but not always in between ("always" is a very important attribute when writing interrupt code). Before implementing the background task, I tried  to make sure that X always represents a valid stack pointer in all primitive words. However, I failed to get it working until I started using a 2nd Data Stack for the background task (which I didn't like since it appears wasteful). Later on, I applied coding techniques that use X for reducing code size. Of course, it's possible to re-factor the code. It would be interesting to compare other multi-tasking Forth implementations. 

I went in a different direction: In several refactoring rounds I removed the following variables entirely: TEMP, XTEMP, PROD1, PROD2, PROD3, CARRY, and I also made the I/O context leaner.

Now, for code without character I/O only YTEMP must be saved. Otherwise also BASE, PAD, and HLD must be taken into consideration. And, of course, we need a stack. One approach would be to have a floating "stack pad" to work around the "X!=TOS" problem.

I guess it will take some time to implement a full featured solution for Forth interrupt handlers.

A minimal solution might look like this:
* a word IVEC! to set an interrupt vector
* a word SAVEC to save the context
* a word RESTC to restore the context, ends with IRET

The application could then define a word in the following way:  

: handler SAVEC ( some stuff ) RESTC ;

 ' handler 1 IVEC! \ set the AWU interrupt handler

Now that I'm looking at it, this doesn't look too bad.

Edit: I made some corrections, added some details, and added one more option for a solution

  Are you sure? yes | no

Thomas wrote 01/25/2017 at 22:23 point

I added the solution above for testing to the develop branch. Due to the mentioned limitations it's currently necessary to initialize the interrupt to priority low (0:1) (it shares the data stack with the ticker).

I also changed TIM4 to prio "highest", which might allow to implement all user defined interrupts with priority "high" later on. This would then require 3 data stacks with the sizes normal (console), medium (background task) and small (interrupt handler).

  Are you sure? yes | no

Thomas wrote 01/26/2017 at 20:48 point

@Elliot Williams:

Here is a starting point for Forth code user interrupts and AWU usage:


  : awuint savec awu_csr1 c@ drop restc ;

  ' awuint 1 ivec!

  : initawu 38 awu_apr c! 1 awu_tbr c! 16 awu_csr1 c! ;


When I run HALT with this code, it returns immediately. Since I didn't find the time to make sense of the AWU configuration, I simply took the AWU timing values from the page you mentioned before.

Please not that this currently only works when I run HALT from the console (I still need a solution for the Data Stack problem). Running HALT from the background task would change the contents of the first element on the stack (which would work if the stack were empty).

A quick fix here is to assume that X represents TOS when HALT is executed (which is the case), and skip initializing the stack. Please note that this only works for HALT, and not in the general case.

  Are you sure? yes | no

Thomas wrote 01/23/2017 at 21:51 point

Changes are in the develpp branch on GitHub. The 2.2.6.snapshot release contains new binaries :-)

  Are you sure? yes | no

Youlian Troyanov wrote 01/26/2017 at 04:51 point

please write your long story about esp-14 :)

  Are you sure? yes | no

Thomas wrote 01/27/2017 at 22:19 point

Elliot, in order to get a simple and practical solution, I now propose the following:

1) In RAM code IVEC! (its only used once for setting an interrupt handler

: IVEC! ( a n - -  ) 2* 2* $800A + ! ;

2) Implement HALT as a user word:

: HALT  ( -- ) [ $8E C, ] ;

3) Implement your interrupt handler using SAVEC and RESTC (make sure not to use more than 8 cells on stack)

This will work for any interrupt. Please make sure to change the interrupt down from highest to high.

  Are you sure? yes | no

jaromir.sukuba wrote 01/23/2017 at 05:17 point

Another tip for *possible* STM8 target

I didn't buy this one, haven't seen the schematics, but to me it totally smells like it could have STM8 under the display. Googling for XK-001T-1 didn't bring much info, though.

  Are you sure? yes | no

Thomas wrote 01/23/2017 at 06:18 point

Yes, that's possible. In most cases one won't find any schematics, and also the XH-, XK, M- or B monikers aren't always used the same. There is a small list of modules that are very likely STM8S based in the Details Section of this project (in the section "How can I spot suitable boards?"). If there is any interest, I can publish a list with advertised properties and the "street price".
Edit: here is a link with a picture showing the PCB legend:
Based on the outline of the µC I would expect it's not STM8 but a STC15 based, a µC which I've seen several times on "timer" boards (MCS51-like

  Are you sure? yes | no

Elliot Williams wrote 12/13/2016 at 23:13 point

Got my ESP-14 up and running last week, and then got distracted.  :)

Short story: it's just a STM8 chip and an ESP8266, like it says on the package.  The TX/RX lines are internally connected, so I was running your Forth on the STM8 with the ESP8266 powered down, and running all manner of software on the ESP with the STM8 powered down.

Been thinking about how to use both at once. 

a) Jeelink is a nice transparent serial port over ESP8266, which would provide remote wireless development of the Forth system on the STM8.  The idea of telnetting over WiFi into an STM8 is funny enough that I'm definitely going to do this.

b) Since the serial port is the only way in to the ESP8266, and the STM8 has only that one hardware serial port, I suppose that bit-banged serial or I2C/SPI could be used to talk to the console. I don't know how hard/easy this is. But then you'd have an STM8 that could issue AT WiFi commands, for instance, or run routines in NodeMCU, which might be very cool.

c) The other option is to code up the ESP and STM8 to take turns based on control characters: 0xFE toggles the ESP on/off the line, and 0xFF toggles the STM8, for instance.   This requires modifying _both_ firmwares, but would allow for the console, ESP, and STM8 to share the UART lines and talk to each other.

Just brainstorming so far. No real hacking yet. 

The breakout board I made for the module just fit it onto a breadboard, because I didn't really know what to expect from the module. It will probably want a transistor so that the STM8 can turn off the ESP8266 for power-saving when necessary, and will certainly want at least a jumper for flashing the ESP.  

Thanks for the case insensitive addition, and for do loops! This is a fun system to play around with.

  Are you sure? yes | no

Thomas wrote 12/14/2016 at 19:31 point

Options a) and b) look good to me, especially in combination. How about connecting a PNP transistor for the ESP8266 power supply to PD1/SWIM? Normally one would access the STM8 serial port through ESP-Link, and the ICP interface could be used for direct access to the ESP8266 serial interface by simply pulling down both NRST and PD1/SWIM. Direct serial access to the STM8 could be acchieved by telling it to power the ESP8266 down (this might even work using PD1/SWIM once more, e.g. by using an RC element which can be detected testing its timing).

Option c) would also be possible, but at least one of the devices would have to be able to swap RxD and TxD, and the other devices would need a "tristate" mode on TxD. The Bus approach I took for the W1209 might also work for more than two devices.

A fourth option could be to have a Forth word that issues the initialization AT commands on the STM8, and execute it with 'BOOT.

I hope to find the time for some hardware hacking in the holiday season :-)

  Are you sure? yes | no

Elliot Williams wrote 12/16/2016 at 21:22 point

"esp-link" not jeelink.  Tried it and had a telnet / web-console controllable STM8 running your Forth.  Took like 10 minutes.

Then I spent 3 hours trying to implement something like c) in NodeMCU.

First, I thought I'd set up two TCP connections: one for the ESP to be executed locally, and one to pass through to the STM8.  Didn't work b/c NodeMCU can only do one TCP connection, it seems.

Then I thought I'd use MQTT as the transport mechanism.  But there's some glitch there with MQTT and the UART port not working right.  I'll hack more at it before I give up, but it might be time to move on to MicroPython or ESP Basic for the interactive ESP part.

Anyway, try out the esp-link for the ESP when you get around to it.  It's kinda fun.  It _does_ however leave me wanting a more capable microchip on the remote end.  For another couple bucks, I could get a lot more flash, peripherals, and etc to tether to the ESP.

All of this playing around has helped me refine what's needed in a breakout board for this thing, though.  :)

  Are you sure? yes | no

Thomas wrote 12/16/2016 at 22:35 point

Again great news, and I'm going to test esp-link too. Multiplexing communication through MQTT topics was the first thing that came to my mind. About a year ago I tried working with MQTT and NodeMCU, but I was disappointed with the stability of the platform (though I really liked working with Lua).
I guess that the case for ESP-14 is rather thin: as I mentioned before, it looks more like proof that the ESP8266 wasn't able to meet customer requirements than like the solution the world's been waiting for. But who cares as long as it's fun hacking.
In my opinion, a decent Forth environment on the ESP8266 would be rather attractive: C.H. Ting hacked something recently, but it was just the kernel, not a complete framework with persistent vocabulary (and maybe even with source stored in the Flash memory, and maybe even a JavaScript based IDE served from an embedded web server on the chip).

  Are you sure? yes | no

Thomas wrote 11/28/2016 at 21:33 point

Hi Elliot, it's great to hear that someone got it running, and that the docs were good for a smooth start. Anyhow, congrats for the "STM8EF Blinky"! Did you try to do that BG style, too? 

I had a look at the CAPS issue (yes, I've been thinking about that for a while ;-) ). There are some potential clashes (e.g. PARSE/parse, NEXT,next, ABORT"/abort") but the lowercase words are the hidden "implementation part", and I don't see that their name is set in stone. I decided to name them after their assembly labels (pars, donxt, and aborq). 

New code with lowercase support is on GitHub (just set CASEINSENSITIVE = 1 in If you'd like to give it a try without building, please let me know (I can drop a binary into the files section here). If there are no issues I'll make it the default.

The ESP-14 will be one of my next targets. However, I didn't find the time to make a breakout PCBs with power supply for this module. Controlling the ESP8266 supply through the STM8S003F3 would be cool. If someone with good access to PCB prototyping could do that job I'd be more than happy to contribute some ideas about the schematics.

  Are you sure? yes | no

Elliot Williams wrote 11/29/2016 at 15:57 point

I just got an ESP-14 in the mail from ebay today.  I'll be making a breakout for it sometime in early Dec.  (Right now, I'm churning out HaD articles like mad.)  I'll share when I do.

I still have no idea if it makes any sense to run a (powerful) ESP8266 off of a (much smaller) STM8 chip.  But I'm willing to find out.  :)

I also ordered one of those LED/relay boards. Again, just for fun, but maybe I'll do something with it.

Thanks for thinking about caps.  I'll definitely rebuild and reflash. 

No, I didn't get into the multitasking / backgrounding. I just got the thing up and running, not much more.

  Are you sure? yes | no

Thomas wrote 11/29/2016 at 19:13 point

The ESP-14 is quite strange. I can only guess that an OEM required a solution from Espressif that meets non-functional constraints (e.g. dependability, power consumption, or periphery set) that could not be met by the ESP8266. I don't think that a lack of skilled programmers was the reason. The power consumption of the STM8S003F3 in "active halt mode" is quite low, and for a data logging sensor node a battery life of a year or more with a 100mAh battery might be feasible.

The W1209 boards are really fun, especially with a background task. When you try using STM8EF with it, please let me know if the docs for the single wire half-duplex solution are sufficient.

About the case-insensitive input: you're welcome (the option has a price tag with "23 bytes" on it :-) )

  Are you sure? yes | no

RigTig wrote 12/15/2016 at 10:44 point

I've created an adapter for ESP14 (and ESP12) to 22-pin DIL, if you haven't done anything else yet (see new project here called 'ESP-12 and ESP-14 adapter to DIL'). My ESP14s arrived today!

  Are you sure? yes | no

Elliot Williams wrote 11/28/2016 at 13:54 point

Hiya! Been following along, finally got a few minutes to flash stuff to one of those min-dev boards.  Great fun!  I haven't done anything useful with it yet, but I've gotten the LED blinking, naturally.

One thing that's driving me nuts is the ALL CAPS commands.  Is there an easy way to either a) lower-case them all or b) make it run case insensitively?  Or would that cause namespace clashes? It makes my shift-finger hurt. 

And that's it for now.  I have to say that your directions (combined with some of the links that you list) made it very easy to get up and running with the system.  Thanks!

I'm planning a few Forth columns for HaD, and I'm still collecting chips that have working implementations.  You've added one more to the list. 

Oh, and I've ordered an ESP-14.  We'll see how that goes.  Looks like fun. 

  Are you sure? yes | no

Does this project spark your interest?

Become a member to follow this project and never miss any updates