Céu: a high-level programming language that runs on Arduino

Hello List!

This is my first message here.

Céu is a reactive language targeted at embedded systems.
It aims to offer a higher-level and safer alternative to C.

With Céu, you can write multiple loops in parallel that can wait for an arbitrary number of events and/or time.

The following example blinks 3 leds forever, each with a different frequency:

   pinMode(11, OUTPUT);
   pinMode(12, OUTPUT);
   pinMode(13, OUTPUT);

   parallel do
       loop do
           digitalWrite(11, HIGH);
           await 1s;
           digitalWrite(11, LOW);
           await 1s;
       end
   with
       loop do
           digitalWrite(12, HIGH);
           await 500ms;
           digitalWrite(12, LOW);
           await 500ms;
       end
   with
       loop do
           digitalWrite(13, HIGH);
           await 250ms;
           digitalWrite(13, LOW);
           await 250ms;
       end
   end

I'm looking for embedded systems developers interested in using Céu.
I can help with all support (i.e. coding) to integrate Céu with your platform.

The distribution of Céu already provides support for Arduino.
Céu is free software and is released under the MIT license.

Homepage: http://www.ceu-lang.org/

Arduino: http://www.ceu-lang.org/wiki/index.php?title=Arduino

Git Repo: GitHub - fsantanna/ceu

Thanks,
Francisco

Very interesting! I will have to give it a try.

David

Looks very promising, many people ask for parallelism on the forum

Is Ceu an open source language?

How does it work, Is it translated to C first?

which platforms are supported? WIndows LInux Mac?

Thanks for the feedback!

Some more info about Céu:

  • Open source (MIT license)

  • The compiler translates to C.
    It also requires a binding file (e.g. for Arduino) included in the distribution.
    This gives Céu a good portability.
    Also, it is possible to understand the generated code (but not easily), and even hack it.

  • The footprint is around 2Kb-4Kb of ROM and 100b or SRAM, depending on the language features in use.
    (for each application, the compiler uses #ifdefs)

--
Francisco

Looks interesting, vaguely occam-like.

AWOL:
Looks interesting, vaguely occam-like.

Yes, on the one hand both have this idea of parallelism and communication among processes (trails in Céu).
On the other hand, being derived from CSP, occam's processes execute very differently from Céu's trails.
I believe the synchronous semantics of Céu (a la Esterel) is more appropriate for Arduino applications.

--
Francisco

This looks very interesting. Noob question though: when you use Céu can you use the regular C alongside it? Or is it one or the other but not both?

Very interesting language. Thanks for developing it! I am working on ArduBlock and I am going to take a look at this to see if we can use it as the backend language. I like the parallel construct which is something I have been thinking about doing with the ArduBlock block language.

madvoid:
This looks very interesting. Noob question though: when you use Céu can you use the regular C alongside it? Or is it one or the other but not both?

Yes, it is very easy to use C from within Céu, just prefix the symbol with an underscore:

_digitalWrite(13, _abs(v));

You can also define C functions inside C blocks:

C do
    int my_abs (int v) {
        if (v >= 0)
            return v;
        else
            return -v;
    }
end

_digitalWrite(13, _my_abs(10));

--
francisco

taweili:
Very interesting language. Thanks for developing it! I am working on ArduBlock and I am going to take a look at this to see if we can use it as the backend language. I like the parallel construct which is something I have been thinking about doing with the ArduBlock block language.

Hello David,

I am aware of ArduBlock, it is a great software, and a parallel "control block" would be handy.

I would love to help you integrating Céu as your backend!

Feel free to inquire me about Céu.

--
francisco

Hello,

I wrote a simple comparison of Céu with two multithreading libraries for Arduino:

Céu for Arduino: http://www.ceu-lang.org/wiki/index.php?title=Arduino

--
Francisco

good to see the differences - ideally you should show them in one videp side by side .

a link to the source code used in the demo sketches would be useful for those who want to repeat the test.

Thanks!
I included a link to the complete source files.

Hi,

In your blog you state:

There’s nothing wrong with the RTOSes and the example implementations: the behavior shown in the videos is perfectly valid.

It is not "perfectly valid", in an RTOS such loss of synch is not supposed to happen, the behavior is supposed to be deterministic. I usually run similar tests so it is surprising to see this. Also, I can see at least one obvious error in the ChibiOS code:

chThdSleepMilliseconds(99999);

99999 is out of range for an uint16_t and the thread is placed in the timers delta list incorrectly but probably this is not the cause of the problem. I can't tell because your main() function is not visible and main() threads runs at the same priority of blinkers, any activity in the main thread can shift the timings.

Do you have any other thread running at same or higher priority than blinker threads? In the DuinOS code there is a third thread at same priority that could explain the shift, you should make sure that blinker threads have the highest priority.

Also, about accuracy of timings please read this article:

http://www.chibios.org/dokuwiki/doku.php?id=chibios:kb:timing

Giovanni

The link you provided gives a very similar example to mine and "assumes the do_something() execution time is well below the system tick period and that my_thread() is not preempted":

An obvious solution is to write something like this:

msg_t my_thread(void *param) {

while (TRUE) {
    do_something();
    chThdSleepMilliseconds(1000); /* Fixed interval.*/
  }
}




This example works well assuming that the do_something() execution time is well below the system tick period and that my_thread() is not preempted by other threads that could insert long intervals.
If the above conditions are not satisfied you may have do_something() executed at irregular intervals, for example:

T0…T0+1000…T0+2002…T0+3002…T0+4005…

Also note that the error increases over time and this kind of behavior can lead to anomalies really hard to debug.

The tick preemption time is an implementation detail and your program should not depend on it to behave correctly.
He even suggests reliable alternatives to the code above.

In my post I didn't say that you cannot implement that program correctly with an RTOS.
My point is that it is not as easy as the manuals usually state (e.g., "Look how easy is to have two blinking leds.").

I corrected the ChibiOS implementation to sleep 10000ms in the main thread (avoiding the overflow) and changed the blinking threads to HIGHPRIO.
The behavior is exactly the same (at least visually).
In the DuinOS implementation I changed their priorities and the behavior is similar (still not synchronized).

That said, I also cannot agree that blinking leds should run with the highest priority in an application.

I wonder if someone can reproduce these tests.

Hi,

That said, I also cannot agree that blinking leds should run with the highest priority in an application.

They shouldn't but it makes sure that the measurement is not affected by possible external source of errors.

I performed a test myself, admittedly not on an Arduino but another board with another CPU. In order to make it more "interesting" I ran 4 threads with timings in the order of milliseconds instead of seconds, there is a fifth thread doing a loop too.

This is the code:

#include "ch.h"
#include "hal.h"

static WORKING_AREA(waThread1, 128);
static msg_t Thread1(void *arg) {

  (void)arg;
  chRegSetThreadName("blinker1");
  while (TRUE) {
    palSetPad(GPIOC, 10);
    chThdSleepMilliseconds(100);
    palClearPad(GPIOC, 10);
    chThdSleepMilliseconds(100);
  }
}

static WORKING_AREA(waThread2, 128);
static msg_t Thread2(void *arg) {

  (void)arg;
  chRegSetThreadName("blinker2");
  while (TRUE) {
    palSetPad(GPIOC, 11);
    chThdSleepMilliseconds(50);
    palClearPad(GPIOC, 11);
    chThdSleepMilliseconds(50);
  }
}

static WORKING_AREA(waThread3, 128);
static msg_t Thread3(void *arg) {

  (void)arg;
  chRegSetThreadName("blinker3");
  while (TRUE) {
    palSetPad(GPIOC, 12);
    chThdSleepMilliseconds(25);
    palClearPad(GPIOC, 12);
    chThdSleepMilliseconds(25);
  }
}

static WORKING_AREA(waThread4, 128);
static msg_t Thread4(void *arg) {

  (void)arg;
  chRegSetThreadName("blinker4");
  while (TRUE) {
    palSetPad(GPIOC, 13);
    chThdSleepMilliseconds(5);
    palClearPad(GPIOC, 13);
    chThdSleepMilliseconds(5);
  }
}

/*
 * Application entry point.
 */
int main(void) {

  /*
   * System initializations.
   * - HAL initialization, this also initializes the configured device drivers
   *   and performs the board-specific initializations.
   * - Kernel initialization, the main() function becomes a thread and the
   *   RTOS is active.
   */
  halInit();
  chSysInit();

  palSetPadMode(GPIOC, 10, PAL_MODE_OUTPUT_PUSHPULL);
  palSetPadMode(GPIOC, 11, PAL_MODE_OUTPUT_PUSHPULL);
  palSetPadMode(GPIOC, 12, PAL_MODE_OUTPUT_PUSHPULL);
  palSetPadMode(GPIOC, 13, PAL_MODE_OUTPUT_PUSHPULL);

  chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);
  chThdCreateStatic(waThread2, sizeof(waThread2), NORMALPRIO, Thread2, NULL);
  chThdCreateStatic(waThread3, sizeof(waThread3), NORMALPRIO, Thread3, NULL);
  chThdCreateStatic(waThread4, sizeof(waThread4), NORMALPRIO, Thread4, NULL);

  while (TRUE) {
    chThdSleepMilliseconds(10);
  }
}

I didn't have a metronome at hand so I had to use a logic analyzer :slight_smile: this is the measurement after 30 minutes of runtime.

Time scale 10mS, no phase shift among threads:

Time scale 10uS, here it is visible the context switch latency that manifests itself as a 10uS jitter:

This without using any particular trick, the five threads all run at same priority.

Giovanni