Teletype firmware discussion

edit 11/3/18:
updated thread title to better reflect the purpose of this thread as a place to ask questions about and discuss teletype firmware.

Hello all,

I recently acquired a Teletype as the command line interface and open source firmware was a big draw for me as a software developer. This community looks great and I’d really like to get involved somehow, or at the very least learn how to implement my own modifications.

I’ve been reading through the Teletype and Avr32 documentation, but figured someone here might be able to point me towards where a good place to start would be. My software development experience is mostly in embedded systems and operating systems, specifically with Infineon microcontrollers.

Step one is making sure you have a functional build chain by rebuilding the current firmware and flashing your device. There are several existing threads which should be of use such as:

…the readme in the teletype repository has more details:

…and finally:

https://monome.org/docs/modular/dev/

2 Likes

yeah, definitely try and set up the toolchain. which os do you use?

i would probably look at one of the trilogy firmwares first, like white whale. they’re a lot smaller but will give a good idea in terms of how things are set up and how to talk to hardware (teletype toolchain will work for trilogy/ansible but not the other way around). experience with embedded devices will definitely help.

this could also be helpful: https://github.com/scanner-darkly/monome-mods/tree/master/arc_ww_init it’s a completely stripped down version of ww firmware changed to work with arc, it was meant as a template but it’s pretty old by now, but there are comments that explain how things set up.

for teletype afaic we don’t really have the architecture described anywhere (maybe in some older threads by @sam?) , we really should have something. i’ll do a write up in the next couple of days.

1 Like

Awesome thanks for the quick responses. I’ve already gotten the toolchain and been able to make successfully, haven’t tried flashing yet that’ll definitely be my next step. I mainly use OSX, but have Linux experience and am open to switching if it’s advisable.

@ngwese Your link to the development docs bring up what my next step was going to be which is getting a debugging environment working. Would this be a good next step in addition to flashing a build, and are there any tips for going about this or should I just follow the monome/libavr32 readme?

@scanner_darkly Starting smaller is really good advice thanks for the resource. A breakdown of the teletype architecture would be great even if brief it would help with understanding the flow.

Amongst other things I was curious how often changes to the libavr32 are necessary or useful?

Debugging on hardware is “printf” style debugging only I’m afraid. You need an FTDI cable, there are detail on the libavr32 page.

If you’re more interested in the programming language side of the Teletype, then the best way to get a handle on it is to start by concentrating on the simulator, that can run on your computer and you can use gdb if needed too.

Essentially the Teletype language side of the codebase lives in the src directory, module contains the code specific to the hardware.

Start with main in simulator/tt.c:

You can see that it sets up a global scene_state_t, then for each command it creates an exec_state_t and then runs the parsed command in it. Don’t worry if that takes a while to comprehend.

Once you’re happy with that, I’d suggest studying src/teletype.c, in particular run_script and process_command. Unfortunately these have become more complex over time as features have been added and bugs have been fixed.

The usual ‘my first Teletype patch’ is to add an OP, either a toy one, or perhaps something particular to your own requirements (it’s fairly easy to maintain your own branch of the Teletype firmware with custom OPs if you’re proficient with git).

If you have something specific you’re trying to achieve let us know and we can point you in the right direction.

4 Likes

libavr32 is shared by all the monome eurorack modules and aleph. typically it changes only when there is a bug fix or some new need (iirc the latest such change was to allow to use your own buffer for grid/arc LEDs), which at this point doesn’t happen very often. what did you have in mind?

Nothing particular. Most of my embedded systems experience is with driver software that my employer’s customers use for their applications, so strangely enough some of what’s going on in there interests me.

Much appreciated Sam. No ideas as of yet just want to understand how things work, so that should keep me busy for a bit.

1 Like

sorry this took a bit longer to get to. this should be a good starting point.


firmware itself is located in:

\module - hosts all files related to the module itself. they are responsible for:

  • running the main event loop
  • executing the currently loaded scene
  • communicating with hardware (screen, inputs, outputs, knob, i2c, keyboard, grid)
  • user interface

\src - hosts the script execution engine files. this is where ops logic is defined. this is hardware agnostic. for anything that requires interacting with hardware it does so through teletype_io.h

then we have:

  • \simulator - a simple app that allows command line interaction with the script engine
  • \tests - a set of tests for validating ops definitions and some logic
  • \docs - markdown files for documentation
  • \utils - various utility scripts and miscellaneous items

in \module we have:

  • edit_mode - script editing UI
  • flash - saving to / loading from flash
  • globals - global variables and functions implemented in main.c (other files can also have their own global vars and functions)
  • grid - grid integration
  • help_mode - help screen UI
  • line_editor - the line editor used by other UIs
  • live_mode - UI for live mode
  • main.c - main program
  • pattern_mode - tracker UI
  • preset_r_mode - UI for loading presets
  • preset_w_mode - UI for storing presets
  • screensaver_mode - screensaver UI
  • usb_disk_mode - saving to / loading from USB stick

main.c is the main engine:

  • initializes and communicates with hardware
  • executes scripts
  • presents the currently selected UI (passes keyboard input, updates screen once it’s rendered by the UI)

in /src we have:

  • /ops - implementation for each op
  • scene - definition for scene and a bunch of scene utility functions
  • teletype_io.h - interface abstracting hardware functions
  • teletype - script engine
  • the rest is helper classes
8 Likes

Thank you! This is going to save me so much time.

Alright hopefully I have this right if someone could verify before I get too far ahead of myself. If I want to add my own OP I believe the following are all the required steps.

From the Teletype README

  1. Add reference to the OP struct in op.c
  2. Generate op_enums.h using op_enums.py
  3. Add the match token entry to the the ragel list in match_token.rl

Using a hardware OP for example here
4. Add extern declaration to hardware.h
5. Add set/get prototypes to hardware.c
6. Define the struct in hardware.c using the MAKE_GET_SET_OP macro
7. Define the get and set functions in hardware.c

3 Likes

I can’t answer your question, but I’d like to say that I appreciate your detailed narration… very helpful for any hypothetical future attempts at firmware mods!

1 Like

Yes that basically seems correct. Run the tests in tests directory (make test), some of the tests check that the tables are filled in correctly. (Make sure the tests are working before starting too.)

Also, make sure you have a solid understand of the difference between the get and set operations. You must always have a ‘get’ even if it feels like you only need a set.

1 Like

Alright looking to get some advice on this if possible. I wanted to create a MOD based on Delay which would allow me to chain delay a command. So for example:

Format: DEL.CHN x y: …

DEL.CHN 500 3: ADD X 1
Would create 3 delays for the command ADD X 1
The first delayed command would occur at 500ms, the next at 1000ms, and the third at 1500ms

I think the code should hold up, havn’t tested it yet because I wanted to make sure I wasn’t doing something ill-advised here.

static void mod_DEL_CHN_func(scene_state_t *ss, exec_state_t *es, 
				command_state_t *cs,
				const tele_command_t *post_command) {
    int16_t i = 0;
    int16_t a = cs_pop(cs); //delay time
    int16_t b = cs_pop(cs); //number of chained delays
    int16_t c; //incremented delay time 

    if (a < 1) a = 1;
    c = a;

    // 0 is the magic number for an empty slot.
    // Be careful not to set delay.time[i] to 0 before calling this function.
    while (ss->delay.time[i] != 0 && i != DELAY_SIZE) i++;

    while (i < DELAY_SIZE && b > 0) {
        ss->delay.count++;
        ss->delay.time[i] = c;
        ss->delay.origin_script[i] = es_variables(es)->script_number;
        ss->delay.origin_i[i] = es_variables(es)->i;
        copy_command(&ss->delay.commands[i], post_command);
        tele_has_delays(ss->delay.count > 0);
	
	    c += a; //increment delay time for next chained delay
	    b--; //decrement delay chain count
	    i++;
    }
}
3 Likes

I think that looks fine. You could move the call to tele_has_delays(...) out of the while loop, but otherwise give it a go!

Multiple delayed commands would be an extremely useful addition. Great idea!

Nice! This would basically cover some of the (long time ago) proposed autotrig (AT) territory iirc.

thinking more about this… can the delay time be modified during execution? that would be insanely awesome imo.

Not sure I understand, can you give an example?

Perhaps others in this thread might consider giving the new developer some time to find their feet before requesting things?

4 Likes

I think @sakul is asking if a buffered command can have its delay time modified/reset before the command fires. I don’t see any need for that, really, given the possibility of using DEL.CLR (the existing command for clearing the delay buffer).

You’ve made a nice ratcheting command, BTW!