Digital Piano “Repair” with Custom Programmed Microcontroller

In this post, I write about a small do-it-yourself project I did end of last year: the repair of a defective digital piano I got. Below is a picture of the internals of the current state in which I replaced the defective parts with a custom programmed microcontroller.

Note: this should go without saying but just in case, before reading on, please be aware that this is not intended as a guide. Working with electrical equipment can be dangerous to you and your environment. If you still try to do anything of this you do this on your own responsibility and at your own risk!

It all started by purchasing a defective digital piano for a reasonably low price given that it did not work. My hope was that the problems could be fixed with reasonable effort. The first checks showed that the fuse was good. An on-board battery was empty. Unfortunately, replacing the battery was not enough to get the piano working again.

After some time without any further clues what could be wrong, I took this as a chance to get myself a used oscilloscope, which is a nice toy of its own.

It showed that one quartz was also defective. I could replace the quartz and it indeed helped a bit in the sense that the piano now could at least enter self-diagnostic. The self-diagnostic showed that RAM and ROM were OK.

Unfortunately, I think I broke something else on the way to this state such that it was still not possible to get the piano working. The self-diagnostic failed while testing the main chip that was responsible, e.g., for the audio generation.

As a kind of “Plan B”, I switched to a different approach: replacing the defective electronic parts with a custom programmed microcontroller. The idea is to read-out the piano keyboard and pedals and generate MIDI messages from that via the microcontroller. These MIDI messages are interpreted with a computer for generating the audio, which can be fed back to the piano amplifier and speakers.

As computer I use a Linux tablet with QSynth for generating the audio. This has the nice side-effect that it can serve as a “touch panel” for the keyboard that can also be used, e.g., for showing learning videos.

I chose an Arduino Mega 2560 as microcontroller because I needed many I/O ports. Below is a picture of a preliminary state.

It took me some experimenting and I even disassembled the keyboard partially to get a proper understanding of the pin out etc. On the Internet, the maintenance manual for the keyboard is available, which also provides valuable information on pin-outs etc. However, I do not link it here because, as said above, this is not intended as a guide.

Eventually, I reached a state with which I am reasonably happy. The current solution also detects the “strength”/”speed” of a key press and puts that information in the MIDI messages.

Last but not least, I also connected the front panel buttons and LEDs to the microcontroller. Because I control all settings via the Linux-based tablet that I use for sound generation, I do not have any use for the buttons so far. However, I programmed the LEDs to display some “idle screensaver” like animation after some time when nobody is playing.

As I am mostly programming in Java, Clojure, Python, and even some JavaScript these days, it was a nice change to do something with a microcontroller that involved stuff like interfacing with GPIO. The source code for the microcontroller is available on GitHub.

Posted in Misc. | Tagged | Leave a comment

cli4clj 1.7.2 – “GraalVM-ready”

In this post, I briefly summarize my experience of making cli4clj “GraalVM-ready”.

Inspired by Jan Stepien’s talk about GraalVM at :clojureD 2019, I got curious about GraalVM. So, I gave it a try to build a “native” executable of the cli4clj example.

The part of GraalVM that interested me, right now, was the capability to build native executables for JVM-based software. However, this post is not about introducing GraalVM. For more information about GraalVM, see, e.g., the GraalVM website or Jan’s blog post on Clojure with the GraalVM.

For building the native image, I first created a standalone Jar via “lein uberjar”. Afterwards, I used GraalVM’s native-image to build the native image:

~/r/p/c/cli4clj ((ec7ba86a…)) ~/Downloads/graalvm-ce-1.0.0-rc12/bin/native-image --no-server --report-unsupported-elements-at-runtime -jar target/cli4clj-1.7.2-standalone.jar 
[cli4clj-1.7.2-standalone:17220]    classlist:   5,198.22 ms
[cli4clj-1.7.2-standalone:17220]        (cap):   1,395.43 ms
[cli4clj-1.7.2-standalone:17220]        setup:   3,496.17 ms
[cli4clj-1.7.2-standalone:17220]   (typeflow): 335,004.07 ms
[cli4clj-1.7.2-standalone:17220]    (objects):  31,205.97 ms
[cli4clj-1.7.2-standalone:17220]   (features):     746.55 ms
[cli4clj-1.7.2-standalone:17220]     analysis: 368,574.15 ms
[cli4clj-1.7.2-standalone:17220]     universe:   7,927.50 ms
[cli4clj-1.7.2-standalone:17220]      (parse):   9,650.90 ms
[cli4clj-1.7.2-standalone:17220]     (inline):   5,801.23 ms
[cli4clj-1.7.2-standalone:17220]    (compile):  50,067.12 ms
[cli4clj-1.7.2-standalone:17220]      compile:  67,484.15 ms
[cli4clj-1.7.2-standalone:17220]        image:   4,716.99 ms
[cli4clj-1.7.2-standalone:17220]        write:     552.31 ms
[cli4clj-1.7.2-standalone:17220]      [total]: 458,269.09 ms

However, to make this work, I had to do some adjustments to cli4clj to “make GraalVM happy”:

  • Add type hints to avoid reflective calls.
  • Downgrade Clojure from 1.10 to 1.9 as, currently, Clojure 1.10 does not work with GraalVM.
  • Add a customized version of clojure.main/skip-whitespace, which adds type hints and removes the “readLine” case.

Furthermore, there is still a minor lurking issue. The verbose exception printing, which is disabled by default, will not work yet as clojure.stacktrace/print-cause-trace is not “GraalVM friendly” yet. I did not address this aspect yet as it only concerns a minor use case and I wanted to experiment a bit first. In future, if GraalVM support proofs valuable, I will try to fix this.

As a result of the native image generation, I got an ~37 MB executable of the cli4clj example. Note that I used dynamic linking on Linux, which caused the following shared object dependencies:

~/r/p/c/c/dist (master=) ldd cli4clj-1.7.2-standalone
	linux-vdso.so.1 (0x00007ffdc9fc6000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f31d15d2000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f31d15cc000)
	libz.so.1 => /lib64/libz.so.1 (0x00007f31d15b2000)
	libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f31d1577000)
	librt.so.1 => /lib64/librt.so.1 (0x00007f31d156d000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f31d13a7000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f31d161d000) 

So far, I could run the generated executable on Fedora and in Windows with the Windows Subsystem for Linux using a Debian basis.

Below is a simple comparison of running the native executable produced by GraalVM versus the standalone Jar run via Java:

r/p/c/c/dist (master=) time ./cli4clj-1.7.2-standalone
cli# q
0.03user 0.06system 0:03.38elapsed 2%CPU (0avgtext+0avgdata 50488maxresident)k
0inputs+8outputs (0major+9253minor)pagefaults 0swaps

~/r/p/c/c/dist (master=) time java -jar cli4clj-1.7.2-standalone.jar 
cli# q
6.33user 0.20system 0:06.27elapsed 104%CPU (0avgtext+0avgdata 183840maxresident)k
96inputs+8outputs (0major+45981minor)pagefaults 0swaps

Note that the reported system time is rather meaningless as the cli4clj example does not automatically terminate but waits at the command input prompt. So, the reported system time mainly depends on how quickly I pressed “q” for terminating the example application.

The other reported values are not impacted that notably by this peculiarity.

The start-up time of the native version is notably faster, which can also be observed in the reported user time and CPU usage. Furthermore, the memory footprint of the native version is less than a third of the Jar file counterpart.

I uploaded a native build of the cli4clj example application and the corresponding standalone Jar to the cli4clj repository.

Furthermore, now (starting with version 1.7.2), you can include cli4clj in your GraalVM-based command line applications.

I hope you find this blog post and cli4clj useful. If you have constructive feedback please let me know.

Posted in cli4clj, Libs. | Tagged , , , , | Leave a comment

cli4clj – @clojuredconf Wrap-up

I just returned from the fantastic :clojureD 2019 conference.

As a wrap-up of my lightning talk on cli4clj, I uploaded the slides of my lightning talk: cli4clj – Easing the Implementation of Interactive Command Line Interfaces in Clojure for “Everyone”

I also added the LaTeX files for the presentation to the master branch of the cli4clj repository. In addition, I uploaded the minimal-example source code and tests shown in the talk.

If you want to try the example yourself, you can clone the clojured_2019 branch of the cli4clj repository and run the example via “lein run” (for the normal scrolling mode) and “lein run — alt” (for the alternate scrolling mode).

Huge thanks to everyone (organizers, attendees, speakers, etc.) involved in :clojureD. It was an honour to get the opportunity to present cli4clj at :clojureD. I enjoyed the conference a lot and had a great time exchanging thoughts and ideas with the lovely community.

Posted in cli4clj, Libs. | Tagged , , | Leave a comment

cli4clj 1.7.0 – Alternate Scrolling Mode

In this post, I briefly introduce a newly added “alternate” scrolling mode for cli4clj, a library for easing the implementation of interactive command line interfaces (CLIs) for Clojure applications.

Since quite some time, I had been a bit unhappy about how repeated and asynchronous output is handled for CLIs implemented with cli4clj. The main cause of the problem is that, simply speaking, everything (commands, command output, completion hints, user input, etc.) is printed into the same shell. This causes the situation that repeatedly printed asynchronous output interferes with user input. Which ultimately causes the problem that the interactive CLI can become hardly usable or completely unusable. Below, an animated gif illustrates the problem.

To solve this situation, I added a first version of an “alternate” scrolling mode. The idea of the alternate scrolling mode is to split the shell into different areas:

  • one for showing “scrolling” output
  • one for entering and editing commands,
  • and one for displaying completion hints and “special” output such as error messages.

Below an example of the alternate scrolling mode is shown.

The largest area at the top above the line is used for printing the “scrolling” output. Below the line, input can be entered at the usual prompt. Below the prompt, “special” output such as completion hints or errors are printed.

The alternate scroll mode can be enabled by setting the key “:alternate-scrolling” to “true” in the cli4clj options map. Alternatively, a predicate function returning a Boolean value can be set.

In addition, the height of the area underneath the prompt reserved for the special output can be adjusted by setting the “:alternate-height” key to a corresponding integer value.

You can try the alternate scrolling mode yourself in the example application by passing “alt” as argument, e.g., by starting it via Leiningen as follows: “lein run — alt”

With the alternate scrolling, I am mostly scratching my own itch. However, I hope that some of you will also have good use cases for the alternate scrolling mode. I would be very happy to hear if cli4clj is helpful for you or if you have constructive comments or feedback.

Posted in cli4clj, Libs. | Tagged , , | Leave a comment

cli4clj Version 1.5.3 – Mixing cli4clj- and Clojure-style Syntax

cli4clj aims on easing the implementation of interactive command line interfaces (CLIs) targeting “everyone” as CLI users. One implication of “targeting everyone” is that CLIs implemented with cli4clj should be usable by “non-lispers”. Thus, cli4clj purposely does not use lisp- or Clojure-style syntax for the commands in the interactive CLI, even though it uses the Clojure REPL underneath.

Yet, people familiar with Clojure or Lisp may miss the functionality of the “full” Clojure REPL. For me, the point of missing the full Clojure REPL was reached when I had to enter quite a long list of almost repetitive arguments in one of my applications.

To counter this, cli4clj had for quite some time the, admittedly pretty hidden, :allow-eval option, which defaults to false. Setting :allow-eval to true causes that “classical” Clojure input will also be processed in cli4clj-based CLIs.

However, even with :allow-eval true, the “cli4clj world” was split into two parts, the cli4clj commands configured in the cli4clj configuration map and the “full” Clojure REPL. It was not possible, e.g., to use cli4clj commands with the Clojure-style syntax.

With cli4clj version 1.5.3, two things changed:

  • :allow-eval now defaults to true.
  • cli4clj commands can be used with the Clojure-style syntax.

To illustrate the effect of these changes, I use the print-cmd from the example CLI provided with cli4clj. print-cmd takes one or more arguments and prints some information about the supplied arguments. Below, the definition of print-cmd is shown:

 
...
 :print-cmd {:fn (fn [arg & opt-args]
                   (print "Arg-type:" (type arg) "Arg: ")
                   (pprint/pprint arg)
                   (print "Opt-args: ")
                   (pprint/pprint opt-args))
             :short-info "Pretty print the supplied arguments."
             :long-info "This function pretty prints its supplied arguments. It takes at least one argument."}
...

One use case of the mixed cli4clj- and Clojure-syntax is to ease the assembly of almost repetitive argument lists. Below, an example for programmatically assembling and applying a long argument list is shown:

 
cli# (apply print-cmd (map #(str "arg_" %) (range 1 10)))
Arg-type: java.lang.String Arg: "arg_1"
Opt-args: ("arg_2" "arg_3" "arg_4" "arg_5" "arg_6" "arg_7" "arg_8" "arg_9")
cli#

Similarly, arguments can now also be stored at first in a var and passed to the command in a second step:

 
cli# (def args (map #(str "arg_" %) (range 1 10)))
#'user/args
cli# (apply print-cmd args)
Arg-type: java.lang.String Arg: "arg_1"
Opt-args: ("arg_2" "arg_3" "arg_4" "arg_5" "arg_6" "arg_7" "arg_8" "arg_9")
cli#

Another use case is the repetitive execution of a command. This is shown below for a comparably small number of repetitions:

 
cli# (doseq [x (range 1 4)] (print-cmd x))
Arg-type: java.lang.Long Arg: 1
Opt-args: nil
Arg-type: java.lang.Long Arg: 2
Opt-args: nil
Arg-type: java.lang.Long Arg: 3
Opt-args: nil
cli#

I hope these changes achieve a good compromise between still keeping cli4clj easy to use for basic use cases while enabling more sophisticated functionality for advanced use cases. If you have constructive feedback or comments, please let me know.

Posted in Announcements, cli4clj, Libs. | Tagged , , | Leave a comment

test2junit Version 1.4.0 Released

This is a bit delayed announcement of the release of test2junit 1.4.0. test2junit lets you “Emit Clojure test output in Junit XML format and optionally automatically invoke HTML generation.”

With some version 1.3.x of test2junit, I added a bit more colorful command line output. What I did not consider back then was that this output may “break” test2junit in cases in which its output is redirected, e.g., when running it in a continuous integration environment.

With version 1.4.0, I added an option to silence the test2junit command line output. To silence the output, set “:test2junit-silent true” in your project.xml.

I hope this update is useful for you and fixes issues for people for which the 1.3.x versions caused trouble. If you have constructive feedback, please let me know.

Posted in Libs., test2junit | Tagged , , | Leave a comment

cli4clj 1.4.0 Released – Now with Persistent Command History by Default

cli4clj aims on quickly and effortlessly creating interactive Command Line Interfaces (CLIs) for Clojure applications. It is based on jline2 and the Clojure REPL and tries to provide (hopefully) reasonable abstractions and defaults. Features of cli4clj include, e.g.:

  • simple configuration via maps,
  • automatic setup of jline2 features such as tab completion or persistent command history,
  • possibility to use Clojure data types as command arguments,
  • functionality for testing interactive CLIs,
  • or customizability similar to the Clojure REPL.

For more details, have a look at earlier posts about cli4clj and the cli4clj github repository.
In this post, I briefly announce the newly added persistent command history, which leverages jline2s FileHistory.

Like the other functionality provided by cli4clj, I tried to provide reasonable defaults in order to ease the use of cli4clj. I hope that this somewhat follows the philosophy of some tools I like such as Leiningen or the fish shell.

The default file name for the file in which the command history is stored is based on the name of the namespace in which the CLI is created. The naming scheme is as follows: (str (System/getProperty “user.home”) “/.” *ns* “.history”)

The naming scheme can be overridden by setting the :history-file-name key in the options map passed to start-cli when creating the CLI. When passing :history-file-name, the full path of the history file has to be given as string.

The command line history can be disabled by setting the :persist-history key in the options map to false. For embedded CLIs, the persistent command line history is always disabled as a history does not make sense in this case.

I hope cli4clj is helpful for you. I am always happy to get constructive comments and feedback.

Posted in cli4clj, Libs., Uncategorized | Tagged , , | Leave a comment

Emumaster for SailfishOS, Work-in-progress

I noticed that I did not publish a post about some porting work I did for Emumaster since quite some time. In this post, I provide mostly pointers to further resources such as source code repository, build repository, and forum thread.

To be clear: I am not the original author of Emumaster. To the best of my knowledge, Emumaster for Nokia N9 was first announced by the user “elemental” in the following thread at talk.maemo.org: http://talk.maemo.org/showthread.php?t=81136 The Emumaster announcement also links to a Wiki page, which, unfortunately, does not seem to exist anymore: https://bitbucket.org/elemental/emumaster/wiki

All credits etc. go to the original author. My contribution is only some very minor work on porting Emumaster to SailfishOS and my attempt to provide recent builds that can be used with updated SailfishOS versions.

For those who never came across Emumaster yet: Emumaster is an emulator that enables running software for, e.g., NES, SNES, PSX, or Amiga, on supported platforms. Currently, the only supported platform for the Emumaster build announced here is SailfishOS.

Below is a list of the most important resources regarding the Emumaster SailfishOS version:

One important limitation is that Emumaster requires armv7hl. Thus, it is likely not to work on armv7l devices, such as the Jolla C community device. Interestingly, on a quick try, the PSX emulator still worked on the Jolla C. For a bit more information about that have a look at the forum thread linked above.

Why was the port needed? Below, I summarize some of the aspects that had to be addressed to make and keep Emumaster working on SailfishOS:

  • Originally, Emumaster was released for Nokia N9, which ran Harmattan and used Qt4. As SailfishOS uses Qt5, some adjustments had to be made to make Emumaster working on SailfishOS. In addition, there were some first performance aspects that had to be addressed.
  • At some time, some dependencies were updated along with SailfishOS, which required a re-build to keep Emumaster working. If I recall correctly, the major culprit was the update of gstreamer from 0.10 to 1.0.
  • For meaningfully running Emumaster on higher resolution devices some hard-coded limits regarding scaling of controls had to be increased.
  • Plus, there were some further minor workarounds to improve Emumaster on SailfishOS, e.g., fix audio problems via a small hack.

The changes mentioned above are in the stable branch of my source code repository. However, even though, I called the branch “stable”, please be aware that there are still many missing and incomplete bits regarding the Emumaster SailfishOS version.

The master branch contains some more work-in-progress changes such as:

  • Attempts for improving the performance. At some time, in scope of a SailfishOS update, I noticed some performance degradation and tried to improve this by changing the way the frames generated by the emulator are drawn. See also the forum thread linked above for some more elaboration on this.
  • Port the drawing of controls from some C/C++ implementation to QML.
  • Enable running Emumaster on Android.

However, all these things are even more work-in-progress than what can be found on the stable branch. So, please proceed with caution and do not expect that things will work easily.

Unfortunately, due my pretty limited time, I cannot put much effort into updating this. Still, I hope that this is useful for some of you. If you have constructive feedback or comments, please let me know.

Posted in Announcements | Tagged , , | Leave a comment

Playing with Event-driven Web-based 3D-Visualization via A-Frame and bowerick

This year in February, I heard for the first time of A-Frame (at RWDSL@CGO). In a nutshell, A-Frame aims on easing web-based 3D visualisation.

Based on my previous work involving Event-driven Architecture (EDA) and Message-oriented Middleware (MoM), I was curious if A-Frame could be used for visualizing near real-time data from EDA/MoM in a web browser. In this post, I briefly write about some very early experiments and how you can run them on your own.

In this post, my focus is on a simple example, which, I hope, you can easily reproduce on your own computer with a few simple steps. If you are interested in more technical details please refer to:

If everything works out, you should be able to see a simple 3D animation in your web browser. Thereby, the animated coordinates are calculated outside of your browser and sent to the browser via the MoM.

Below the results are shown in two screenshots. In the first screenshot, a simple sphere is used. In the second screenshot, I used a tetrahedron to which I added a “trail” to better visualize the animated movement. Note that I deliberately enabled shadows for the sphere example and disabled shadows for the tetrahedron-trail example.

For easing the reproducibility of the examples, I made the corresponding web-pages available via the github pages of the bowerick project:

Alternatively, you can also find the web pages in the examples directory of the bowerick repository.

In addition to the web pages, you need a MoM broker and an event/message producer for generating messages with the animated coordinates. To ease the use of this example, I included a corresponding event/message generator in bowerick.

In the simplest way of using it, the event/message generator will be created along with a broker instance. Thus, you only need to download an appropriate bowerick version, start the example, and connect to the broker using the example web pages mentioned above. Below, commands for optionally downloading bowerick and running the example are shown:

 
# Optionally, use, e.g., wget or curl to download bowerick:
# wget https://github.com/ruedigergad/bowerick/raw/master/dist/bowerick-2.2.2-standalone.jar
# curl -o https://github.com/ruedigergad/bowerick/raw/master/dist/bowerick-2.2.2-standalone.jar
# Start the example including an appropriate broker:
java -jar bowerick-2.2.2-standalone.jar -A

The example web pages use default settings for the broker URL and topic name that matches this simple setup of running the example on localhost. Thus, to see the animated visualization, you simply need to press the “Connect” button of the example web pages.

For another example and as an outlook on a post I plan on more sophisticated event/message generation, you can try to run the bowerick event/message generation as shown below. As shown in the image underneath, this example plots a heart shape using a more complex mathematical function than the simple circle above (This is actually an Easter egg dedicated to family.).

 
java -jar bowerick-2.2.2-standalone.jar -G heart4family -D /topic/aframe -u "[\"ws://127.0.0.1:1864\"]"

I hope you consider bowerick and this post useful. If you have constructive criticism and comments just let me know. I am very much looking forward to helpful feedback.

Posted in bowerick, Libs. | Tagged , , , | Leave a comment

cli4clj Version 1.3.2 – New: Embedded CLIs

In this post, I announce cli4clj version 1.3.2 and write a bit about the newly added functionality for creating embedded Command Line Interfaces (CLIs). For those unfamiliar with cli4clj: the aim of cli4clj is to ease the implementation of simple interactive CLIs for Clojure applications.

The original and still primary focus of cli4clj is on implementing interactive CLIs intended to be used in interactive terminals/shells. However, in one of my other applications, I needed an interactive CLI that can be used via network, actually via a Message-oriented Middleware (MoM).

The primary aspect in the CLI via network use case, from the cli4clj perspective, is that cli4clj leverages jline for handling interactive input from a terminal/shell. In the CLI via network use case, however, input cannot be read from an interactive terminal/shell.

In the concrete scenario of using an MoM, I wanted to send commands via messages and receive the output via corresponding reply messages. Furthermore, I wanted to use the existing MoM infrastructure without the need of opening yet another socket. Hence, networked CLIs such as nREPL did not appear as a good match to me.

Furthermore, I wanted to re-use the same map-based approach for configuring the jline-based CLI for the “embedded” CLI. The reason for this is that this will allow to easily switch between a jline-based and an embedded CLI or use both at the same time, e.g., when an application is intended to be used interactively in a terminal and, at the same time, the same functionality should be exposed via network. Moreover, I wanted to make the new “embedded” CLI generic such that it can also be used in other places.

The embedded CLI functionality uses the map-based cli4clj configuration format and creates a “CLI-function” instead of an interactive input. The generated function accepts a string of commands and arguments as input and returns the string result of “executing” the provided input. Thereby, the string result is the output that was produced as a result of executing the supplied command and arguments string.

Below, an example for using the embedded CLI functionality is shown. The example was executed in the cli4clj project in a “lein repl”. In order to illustrate that the output is really returned as string from the “CLI-function” I used the indirection of putting the results in the “ret” var and showed them by printing the value of “ret”.

cli4clj.example=> (use 'cli4clj.cli)
nil
cli4clj.example=> (def cli-opts {:cmds {:print {:fn #(println %)} :divide {:fn (fn [x y] (/ x y))}}})
#'cli4clj.example/cli-opts
cli4clj.example=> (def cli-fn (embedded-cli-fn cli-opts))
#'cli4clj.example/cli-fn
cli4clj.example=> (def ret (cli-fn "print foo"))
#'cli4clj.example/ret
cli4clj.example=> ret
"foo"
cli4clj.example=> (def ret (cli-fn "divide 4 2"))
#'cli4clj.example/ret
cli4clj.example=> ret
"2"
cli4clj.example=> (def ret (cli-fn "divide 4 0"))
#'cli4clj.example/ret
cli4clj.example=> ret
"ERROR: Divide by zero"
cli4clj.example=> (def ret (cli-fn "bar"))
#'cli4clj.example/ret
cli4clj.example=> ret
"ERROR: Invalid command: \"[bar]\". Please type \"help\" to get an overview of commands."
cli4clj.example=> (def ret (cli-fn "help"))
#'cli4clj.example/ret
cli4clj.example=> ret
"divide\n\n\nhelp [? h]\n\tShow help.\n\tDisplay a help text that lists all available commands including further detailed information about these commands.\n\nprint\n\n\nquit [q]\n\tQuit the CLI.\n\tTerminate and close the command line interface."
cli4clj.example=> (println ret)
divide
help [? h]
 Show help.
 Display a help text that lists all available commands including further detailed information about these commands.
print
quit [q]
 Quit the CLI.
 Terminate and close the command line interface.
nil
cli4clj.example=>

Besides adding the embedded CLI functionality, I also fixed some issues, as you can see in the version number. However, as things usually go with adding new functionality, even though it was tested and implemented following test-driven development, I likely added new bugs as well. So, whenever you spot a problem, just let me know.

I hope cli4clj is useful for you. I am always happy about constructive feedback and comments.

Posted in cli4clj, Libs. | Tagged , , | Leave a comment