Stream: ideas

Topic: Roc Clock


view this post on Zulip Nick Hallstrom (Jan 26 2023 at 03:08):

So about a year ago I tried to build this clock using some Servos and a Raspberry Pi Zero. It was written in Elixir using Nerves. I got about half way through and never finished it. However, I think I know just the think to motivate me to finish it: rewrite the whole thing in this awesome language called Roc! Plus calling it the Roc Clock would be super awesome :grinning_face_with_smiling_eyes:

I’m wondering if Roc is at a point now where this would be feasible without hitting any major road blocks. There’s already Rust libraries to control the GPIO pins on a Pi so I’m thinking I can just write a platform that exposes some of this to Roc. I believe the Pi is an ARM processor so I’m hoping that isn’t an issue, maybe I’ll have to compile Roc from scratch?

Anyways, let me know what you think. And thanks in advance!

Snapchat-1469939314.mp4

view this post on Zulip Brendan Hansknecht (Jan 26 2023 at 04:07):

Since the raspberry pi zero runs linux (right? haven't played with one in a long time), I am pretty sure this should be doable with current roc without compiler modification.

view this post on Zulip Brendan Hansknecht (Jan 26 2023 at 04:08):

True embedded processors like an esp32 or stm32 is where you would need a custom version of the roc compiler with new targets in order to get it working (though still doable, I was messing with that a long while back)

view this post on Zulip Brendan Hansknecht (Jan 26 2023 at 04:10):

Oh, It looks like we don't have a nightly release for linux arm, so you will at least need to compile the compiler, but you shouldn't need to modify it.

view this post on Zulip Brendan Hansknecht (Jan 26 2023 at 04:11):

Though I guess you could also theoretically cross compile the app as well. Though that may take a bit more fiddling. Not fully sure.

view this post on Zulip Brendan Hansknecht (Jan 26 2023 at 04:12):

Anyway. Generally story is that it should definitely be doable, but will likely have some minor extra hoops to jump through. I don't think it should have any major issues.

view this post on Zulip Ayaz Hafiz (Jan 26 2023 at 04:28):

Agreed with Brendan, I think aside some a new compilation target it should be possible. There are known compiler bugs with highly-recursive programs but I would assume that your problem domain would not run into them.

view this post on Zulip Nick Hallstrom (Jan 26 2023 at 14:09):

Yes it runs Linux so hopefully I’m good there. Not sure if I’ll mess with cross compiling or just do the development on the Pi itself. Thanks for the feedback!

view this post on Zulip Brendan Hansknecht (Jan 27 2023 at 20:00):

Just a follow up here. I looked into this some more with a raspberry pi I have laying around. Currently if you on a 32bit arm device, our nix build environment will not load. It is a known nix bug. Should still theoretically be able to use roc, but you would need to install all dependencies manually. On 64bit arm, nix will work fine.

view this post on Zulip Nick Hallstrom (Jan 29 2023 at 08:34):

Thanks for the update! I believe I have all the dependencies installed, but I’m getting this error now when running cargo build
43029D9D-0B49-4819-8039-33F89A3BA409.jpg

view this post on Zulip Brendan Hansknecht (Jan 29 2023 at 09:05):

Yeah, i was looking into this more, I'll push a branch tomorrow.

view this post on Zulip Brendan Hansknecht (Jan 29 2023 at 09:05):

I managed to get the c hello world running earlier today on 32 bit pi.

view this post on Zulip Nick Hallstrom (Jan 29 2023 at 16:28):

You’re the best! Thanks so much for the help, I really appreciate it. Please let me know when you get that pushed

view this post on Zulip Brendan Hansknecht (Jan 29 2023 at 18:04):

Ok. Just pushed what I currently have to the rpi branch.

A few notes:

Anyway. Definitely here to help as needed. Here was my full workflow:

bren077s@raspberrypi:~/roc $ uname -a
Linux raspberrypi 5.15.84-v7+ #1613 SMP Thu Jan 5 11:59:48 GMT 2023 armv7l GNU/Linux
bren077s@raspberrypi:~/roc $ RUSTFLAGS="-Clink-arg=-fuse-ld=lld" cargo run --profile low-mem --features target-arm -j 2 build examples/platform-switching/rocLovesC.roc
    Finished low-mem [optimized] target(s) in 3.38s
     Running `target/low-mem/roc build examples/platform-switching/rocLovesC.roc`
🔨 Rebuilding platform...
0 errors and 0 warnings found in 2157 ms while successfully building:

    examples/platform-switching/rocLovesC
bren077s@raspberrypi:~/roc $ ./examples/platform-switching/rocLovesC
Roc <3 C!

view this post on Zulip Brendan Hansknecht (Jan 29 2023 at 18:04):

Definitely takes a while to compile especially as you restrict ram more.

view this post on Zulip Brendan Hansknecht (Jan 29 2023 at 18:05):

I probably should look at what roc features can be fully disabled. That would likely cut out a ton of depedencies and make it compile faster

view this post on Zulip Brendan Hansknecht (Jan 29 2023 at 18:06):

Please feel free to ask as many question as needed. I should definitely be able to help as you run into issues.

view this post on Zulip Richard Feldman (Jan 29 2023 at 18:41):

very nice! I wonder if we could put this advice as a .md in the repo linked from the building from source docs, so people can discover it more easily?

view this post on Zulip Richard Feldman (Jan 29 2023 at 18:43):

also I wonder if someday it would be worth doing a nightly build targeting rpi :thinking:

view this post on Zulip Nick Hallstrom (Jan 29 2023 at 18:54):

Wow thanks Brendan! I can tell I’m going to like this community already 😁 I’ll give that a go later today when I get home. A nightly rpi build would be great. I’d love to help get that going if possible. How are the nightly builds set up?

view this post on Zulip Brian Carroll (Jan 29 2023 at 18:59):

Have a look at this file in the repo, and similarly-named ones:
.github/workflows/nightly_linux_x86_64.yml

view this post on Zulip Brendan Hansknecht (Jan 29 2023 at 19:57):

I'm not sure how llvm dependencies would work with this, but it would be awesome if we could setup cross compilation of the compiler. Would make it way easier to generate a number of nightly builds on a single powerful machine.

view this post on Zulip Brendan Hansknecht (Jan 29 2023 at 20:03):

Oh, extra note: it works to do --no-default-features --features target-arm. Reduces the number of packages to compile by about 120.

view this post on Zulip Nick Hallstrom (Jan 31 2023 at 06:45):

Compiling on the Pi is definitely slow :sweat_smile: After a few hours it compiled successfully, but trying to run any of the examples leads to this error
Screenshot_20230130_114318.png

view this post on Zulip Nick Hallstrom (Jan 31 2023 at 06:50):

This was with --no-default-feature by the way. The full command was

RUSTFLAGS="-Clink-arg=-fuse-ld=lld" cargo run --profile low-mem --no-default-features --features target-arm -j 1 build examples/platform-switching/rocLovesC.roc

view this post on Zulip Brendan Hansknecht (Jan 31 2023 at 07:31):

Oh crap. I misguided you slightly. I thought you were on a system that only had 32bit support. You have a system running 64bit. So you want the target-aarch64 and not target-arm feature. Sorry about that.

view this post on Zulip Brendan Hansknecht (Jan 31 2023 at 07:34):

Compiling on the Pi is definitely slow

You should be able to run most if not all of the compilation with -j 4. Though -j 2 is definitely safer if you have 1GB or less of ram. Otherwise it might randomly freeze or crash. That said, it is fine to run -j 4 and the later run -j 2 just for the memory heavy packages that lead to crashes. Still will be slow, but better.

If your pi is swapping a lot, i definitely advise enabling either zram or zswap. They should help a lot to keep thing sin memory and greatly speed up compilation. I would advise enabling them with lz4. zstd is too slow on a pi.

view this post on Zulip Brendan Hansknecht (Jan 31 2023 at 07:36):

Also, hopefully once we get the compiler built right, you won't have to build it again. So mostly a one time cost.

view this post on Zulip Nick Hallstrom (Feb 01 2023 at 02:55):

Progress! But a new error now
Screenshot_20230131_075426.png

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 04:36):

Oh yep. Cool.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 04:36):

Nix being a pain.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 04:37):

Try exiting nix and then manually running the built executable: ./target/low-mem/roc ...

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 04:39):

I think the issue is that nix is pulling in a different version of glibc than what gets pulled in while compiling the platform.

view this post on Zulip Nick Hallstrom (Feb 01 2023 at 05:12):

No luck with that either.
Screenshot_20230131_101153.png

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 06:45):

Hmm. Let me double check. I definitely hit this before and got around it.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 07:53):

Hmm...i guess i didn't fix it on 64bit rpi.

So, the core issue with nix is that it is using glibc version 2.35 while the rpi is at glibc version 2.31

I think when roc compiles the platform and app, it pulls in the rpi glibc instead of nix and that breaks.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 07:55):

For the libxcb issue, i think libxcb is related to x11 and graphics. Kinda surprise that roc with --no-default-features pulls it in. I think it is pulled in by the editor (which should be disabled), but that is all speculation and guessing.

I tried installing the library and I can see the file for it now, but still no luck. So i am quite uncertain.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 07:55):

Need more time to debug.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 07:55):

Maybe @Anton has some nix ideas.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 08:05):

As a note, i was able to have roc dump rocLovesC.roc as an object file and manually link it. So we have all the pieces functioning.

view this post on Zulip Anton (Feb 01 2023 at 08:43):

Nick Hallstrom said:

Progress! But a new error now
Screenshot_20230131_075426.png

Are you still inside nix when you are executing rocLovesC?

It's also possible that your cargo cache contains artifacts that were built outside of nix.

I'd recommend doing:

nix develop
cargo clean
cargo build --release -j 4
./target/release/roc run ./examples/platform-switching/rocLovesC.roc

I also recommend installing starship (outside of nix, so before nix develop), it will show when you are inside the nix shell.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 17:56):

I am pretty sure that won't fix this.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 17:56):

I don't have the tools installed to build outside of nix and I hit this.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 17:56):

So Everything I built had to be within nix.

view this post on Zulip Anton (Feb 01 2023 at 18:06):

Hmm, strange. I have an aarch64 pi, I can set it up on Friday to try to replicate the issue

view this post on Zulip Nick Hallstrom (Feb 01 2023 at 18:09):

So would I have better luck here if I tried this on a 32bit install? I went with 64 because Brendan said something about all the platforms being hard coded to 64 bit

view this post on Zulip Nick Hallstrom (Feb 01 2023 at 19:16):

Confirming that it didn't fix the issue

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 20:07):

I think 64bit is definitely the better option if we can fix this.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 20:07):

To be fair, i know a guaranteed way to fix this. Don't use nix. Just install all the dependencies manually like you would be forced to on a 32bit pi.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 20:08):

Though will require a full clean and recompilation.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 20:09):

So i would advise manually dependency install on 64bit over switching to 32bit.

view this post on Zulip Nick Hallstrom (Feb 01 2023 at 20:22):

I think I’ll try installing the deps manually for now then and see if I can get that working.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 20:28):

Sorry that I mixed that up. I have it fully compiling and running on 32bit with manual deps install. For 64bit, I guess I only ever compiled and forgot to run.

view this post on Zulip Nick Hallstrom (Feb 01 2023 at 23:15):

Woohoo that did the trick!
It's working!

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 23:28):

Yay!

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 23:28):

Hopefully Rust and Zig platforms work as well?

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 23:28):

Though I guess you mostly just care about rust

view this post on Zulip Nick Hallstrom (Feb 01 2023 at 23:35):

Rust platform works. I'll check Zig after. Right now I'm trying to compile one of the basic-cli examples and it's been compiling for about 15 minutes. Should it take that long? Any tips to get this sped up? I've already done the previous stuff like use zram.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 23:39):

Oh, I bet that the platform is compiling in debug mode. Which ends up being slow in low ram due to how much memory especially linking takes.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 23:40):

I would update the Cargo.toml for the platform to modify debug to not actually add debug info

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 23:41):

[profile.dev]
debug = 0
strip = "symbols"

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 23:42):

Then you also can follow the update to .cargo/config.toml to have it use lld which links faster and uses less memory:

[build]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 23:42):

Hopefully those both greatly speed up the time.

view this post on Zulip Brendan Hansknecht (Feb 01 2023 at 23:43):

Lastly, you could manually compile the platform just to see that it is actually making progress. instead of all of the updates getting eaten by the roc executable.

view this post on Zulip Nick Hallstrom (Feb 01 2023 at 23:54):

How do I manually compile it? I've tried similar to what I did before but it's not working:
Screen-Shot-2023-02-01-at-4.53.31-PM.png

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 00:02):

Just cargo build. It won't have all of those features and won't produce a usable executable until final compilation with roc.

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 00:03):

Also, you may hit issue if you directly overwrite the rust flags. When roc goes to finish compiling the platform, it won't end up using your override flag and will probably cause a full recompilation.

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 00:03):

Though when you go back to roc for finalizing, you can alway add --prebuilt-platform=true to hopefully stop it from messing any of that.

view this post on Zulip Nick Hallstrom (Feb 02 2023 at 00:09):

Just got this
Screen-Shot-2023-02-01-at-5.05.01-PM.png

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 00:11):

interesting. I get the feeling I should probably repro this locally at this point and poke around.

view this post on Zulip Nick Hallstrom (Feb 02 2023 at 03:07):

Also, this is the result of me trying the REPL. It starts up and then I just typed ”test” into the prompt and it hangs for a while and then outputs this
Screenshot 2023-02-01 at 8.07.09 PM.png

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 04:27):

repl uses the wasm backend, so you probably don't have it compiled.

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 04:27):

Given we turned off default features

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 06:59):

Ok. Here is my understand of the issue. Some reason despite us passing a relocation mode of PIC to llvm. On aarch64, llvm is generating non-pic relocations. Thus, when linking, we hit a failure. Really confused why llvm is generating the bad relocations, but that is definitely what it looks like to me.

That said, I have a work around at least: comment out this line: https://github.com/roc-lang/roc/blob/rpi/crates/compiler/build/src/link.rs#L1191

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 07:00):

That will create a position dependent executable, which is ok for these cases.

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 07:00):

You must explicitly roc build and then run the app.

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 07:01):

Also, if you at first get a segfault. cd into the platform directory and clean it. So in the case of the cli example (cd examples/cli/cli-platform && cargo clean)

view this post on Zulip Brian Carroll (Feb 02 2023 at 07:53):

repl uses the wasm backend, so you probably don't have it compiled.

We have two REPLs:
The one on the website is a Wasm build of the Roc compiler with the Wasm backend.
The one you get when you do roc repl on the command line is a native build of the Roc compiler with the LLVM backend.

view this post on Zulip Nick Hallstrom (Feb 02 2023 at 20:56):

That seems to have done the trick!
Screen-Shot-2023-02-02-at-1.53.05-PM.png

Next step is to try and get the LED on the pi blinking using Roc I think. I'm just going to try and modify the basic-cli platform unless there's a better way. Seems like it should be pretty straightforward to expand upon from looking through the platform code.

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 21:36):

That sound be a fine way to do it. If types get more complex, roc glue can be very helpful.

view this post on Zulip Nick Hallstrom (Feb 02 2023 at 21:39):

Is there anyway I can learn more about roc glue? I keep hearing about it and I've tried roc glue --help but I'm still now sure exactly how to use it

view this post on Zulip Folkert de Vries (Feb 02 2023 at 21:40):

it's work in progress

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 21:54):

fundamentally roc glue <roc-file>.roc <rust-file>.rs will generate a rust file that maps the roc types to rust types. Thus making it much easier to use the types in rust. That said, it has a number of restrictions, especially currently. None the less, it is extremely useful.

As a simple example: cargo run glue examples/cli/cli-platform/InternalFile.roc examples/cli/cli-platform/src/file_glue.rs
....hmm.... Though this seems broken now. Did we break glue?

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 21:56):

Maybe @Richard Feldman's PR that changed exporting and loading to support url modules?

view this post on Zulip Richard Feldman (Feb 02 2023 at 22:11):

possibly, although at this point from a glue perspective I want to focus on getting the branch working that has the "accept a .roc script" rather than fixing the current one that's on the way out :big_smile:

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 22:13):

That's fair. Shoudn't it affect both though? It would be part of the parsing before we reach the roc file.

view this post on Zulip Nick Hallstrom (Feb 02 2023 at 23:08):

Ok more progress and more questions :) I just added a setPinHigh and setPinLow in Effect.roc and then implemented the rx functions in lib.rs. I'm exposing it through Stdout just for now. It compiles and seems to be working (I'm away right now so I can't actually see the LED on the Pi) but now I'm encountering a weird issue:
This works just fine.
So does this (I think, can't see the LED until I get home tonight).
I'm pretty sure I'm doing this one right but...
it segfaults.

view this post on Zulip Brendan Hansknecht (Feb 02 2023 at 23:49):

Does that consistently segfault. On my pi, I noticed that randomly I was hitting segfaults. I would cargo clean the platform and then magically the segfault would go away. Have yet to dig into or figure out the cause.

view this post on Zulip Nick Hallstrom (Feb 03 2023 at 00:29):

Seemed pretty consistent. I also cargo cleaned it once and it still segfaulted after.

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 02:43):

Interesting...i don't have any immediate thought.

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 04:17):

can you share your repo. I'll take a look.

view this post on Zulip Nick Hallstrom (Feb 03 2023 at 06:08):

Here you go!
https://github.com/Billzabob/roc-clock
Thanks for taking a look! Really appreciate it

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 16:42):

Oh, you just mixed up a type definition for your effects.

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 16:45):

The effect is defined as U8 -> Effect {}, but the rust function of type u8 -> RocResult<{}, {}>.

So the effect should really be U8 -> Effect (Result {} {})

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 16:49):

Also, you definitions in Stdout, should match the definition of setCwd in Env if you don't want the error to just be swallowed.

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 16:49):

Well, i guess it isn't swapped, the full result is just returned in the success case as opposed to take the error path on errors.

view this post on Zulip Nick Hallstrom (Feb 03 2023 at 16:50):

My bad I should have updated you, I figured that out last night. Now I have another weird problem though.
This compiles

app "high"
    packages { pf: "../src/main.roc" }
    imports [pf.Stdout, pf.Stdin, pf.Task.{ Task }]
    provides [main] to pf

main : Task {} []
main =
    _ <- Task.attempt (Stdout.setPinHigh 23)
    _ <- Task.await Stdin.line
    Task.succeed {}

But this segfaults

app "high"
    packages { pf: "../src/main.roc" }
    imports [pf.Stdout, pf.Stdin, pf.Task.{ Task }]
    provides [main] to pf

main : Task {} []
main =
    _ <- Task.attempt (Stdout.setPinHigh 23)
    _ <- Task.await Stdin.line
    _ <- Task.attempt (Stdout.setPinLow 23)
    Task.succeed {}

view this post on Zulip Nick Hallstrom (Feb 03 2023 at 16:51):

I just pushed those changes in case you wanted to take a look

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 17:06):

Oh wow, that is crashing the actual compiler.

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 17:40):

One workaround for now is to use --optimize

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 17:40):

No idea why this fixes it, but it does

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 17:40):

May want to change the platform cargo.toml to have a lighter release build so you don't have to wait forever on compilation.

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 17:41):

Also, --prebuilt-platform=true is useful if you know the platform hasn't changed.

view this post on Zulip Nick Hallstrom (Feb 03 2023 at 19:04):

Oh weird but hey it worked! How do I change it to have a lighter release build?

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 19:33):

Just change the release profile opt level in Cargo.toml for the platform

[profile.release]
opt-level = 1

That or setting it to 0 will case the platform to build a lot faster due to not optimizing as much/at all.

view this post on Zulip Brendan Hansknecht (Feb 03 2023 at 19:34):

Kinda force opt to have dev level of optimization because you really just want it to compile fast.

view this post on Zulip Nick Hallstrom (Feb 04 2023 at 03:05):

So the code below works, almost. The issue is that as soon as this function exits, the GPIO gets reset when pin goes out of scope. The only way I can actually see the GPIO change state is if I throw a sleep after the set_high()call. Even if I make a sleep function that I expose through Roc and compose with Task.await it doesn't work, but that's what I'd like to do ideally. Is there anyways I can call Gpio::new() outside of this function and then just have the function use it, if that makes sense.

#[no_mangle]
pub extern "C" fn roc_fx_setPinHigh(pin: u8) -> RocResult<(), ()> {
    use rppal::gpio::Gpio;

    let mut pin = match Gpio::new() {
        Ok(gpio) =>
            match gpio.get(pin) {
                Ok(p) => p.into_output(),
                _ => return RocResult::err(()),
            },
        _ => return RocResult::err(()),
    };

    println!("setting high");
    pin.set_high();
    RocResult::ok(())
}

view this post on Zulip Brendan Hansknecht (Feb 04 2023 at 03:14):

Two options come to mind:

  1. global that is protected by a mutex. That should work.
  2. Switch to returning commands from main and then have rust switch of those (this is where roc is going in the future in general as opposed to effects functions. That said, it requires more messing around to set up and may not be a task you want to take on before the upgrading version of glue can generate it for you).

view this post on Zulip Nick Hallstrom (Feb 04 2023 at 03:27):

That second option definitely sounds more appealing. Is there any examples of that anywhere? Even if I don’t go that route right now, I’d love to take a look at it

view this post on Zulip Brendan Hansknecht (Feb 04 2023 at 03:43):

Richard talk about it at the last meet up. About 20 minutes in: https://drive.google.com/drive/folders/1OrgVPE6qGx34MT8oP2aNsop1oAs3_EVl

This repo is an old proof of concept that kinda uses the idea but was done manually: https://github.com/bhansconnect/roc-todos

There may be a better example, but I need to dig around and see. The core idea is return a tag union to the platform. The tag union contains a command, some data, and a continuation. The host matches on the command, runs an effect using the data, and then calls the continuation. This enables async state machines and simpler platform development. That said, the types right now can be complex and hard to manage. Once glue is updated, it should all get auto generated.

view this post on Zulip Nick Hallstrom (Feb 04 2023 at 03:47):

Thanks a bunch! I’ll give it a watch when I get a chance 👍

view this post on Zulip Anton (Feb 04 2023 at 08:50):

fyi I tried building on my aarch64 rpi yesterday but ran out of disk space. I'm going to order a larger microsd today.

view this post on Zulip Nick Hallstrom (Feb 08 2023 at 05:45):

Got an LED blinking from Roc:
Blinking LED Video

app "blink"
    packages { pf: "../src/main.roc" }
    imports [pf.Gpio, pf.Task.{ Task }]
    provides [main] to pf

pin = 18

main : Task {} []
main = Task.forever blink

blink : Task {} []
blink =
    _ <- Task.attempt (Gpio.setPinHigh pin)
    _ <- Task.await (Gpio.sleep 1000)
    _ <- Task.attempt (Gpio.setPinLow pin)
    _ <- Task.await (Gpio.sleep 1000)
    Task.succeed {}

view this post on Zulip Nick Hallstrom (Feb 08 2023 at 05:57):

Controlling LED intensity with hardware PWM:
PWM Video

app "pwm"
    packages { pf: "../src/main.roc" }
    imports [pf.Gpio, pf.Stdout, pf.Stdin, pf.Task.{ Task }]
    provides [main] to pf

frequency = 50

main : Task {} []
main = Task.forever setPwm

setPwm : Task {} []
setPwm =
    dutyCycleResult <- Task.attempt getDutyCycle
    when dutyCycleResult is
        Ok dutyCycle      -> setDutyCycle dutyCycle
        Err InvalidNumStr -> Stdout.line "Duty cycle is invalid. Try again.\n"

getDutyCycle : Task F64 [InvalidNumStr]
getDutyCycle =
    _            <- Task.await (Stdout.line "Set the duty cycle from 0.0 to 1.0:")
    dutyCycleStr <- Task.await Stdin.line
    Task.fromResult (Str.toF64 dutyCycleStr)

setDutyCycle : F64 -> Task {} []
setDutyCycle = \dutyCycle ->
    str = Num.toStr dutyCycle
    result <- Task.attempt (Gpio.pwm frequency dutyCycle)
    when result is
        Ok {}          -> Stdout.line "Duty cycle is: \(str).\n"
        Err PwmFailure -> Stdout.line "Failed to set PWM"

view this post on Zulip Luke Boswell (Feb 08 2023 at 05:59):

Super cool. Video doesn't load for me. But looks really cool :D

view this post on Zulip Nick Hallstrom (Feb 08 2023 at 06:06):

Neither of the videos load for you? Hmmm... Do video uploads not work in Zulip?

view this post on Zulip Luke Boswell (Feb 08 2023 at 06:08):

Maybe not, it just waits a second or two and then Zulip dings, but I can't see anything changing.

view this post on Zulip Nick Hallstrom (Feb 08 2023 at 06:08):

Controlling a single servo with PWM:
PWM Servo

app "servo"
    packages { pf: "../src/main.roc" }
    imports [pf.Gpio, pf.Stdout, pf.Stdin, pf.Task.{ Task }]
    provides [main] to pf

frequency = 50

main : Task {} []
main = Task.forever run

getRotationAmount : Task F64 [InvalidNumStr]
getRotationAmount =
    _              <- Task.await (Stdout.line "Set the rotation amount from 0 to 180:")
    rotationAmount <- Task.await Stdin.line
    Task.fromResult (Str.toF64 rotationAmount)

run : Task {} []
run =
    rotationAmountResult <- Task.attempt getRotationAmount
    when rotationAmountResult is
        Ok rotationAmount -> setRotation rotationAmount
        Err InvalidNumStr -> Stdout.line "Duty cycle is invalid. Try again.\n"

setRotation : F64 -> Task {} []
setRotation = \rotationAmount ->
    dutyCycle = map rotationAmount 0 180 0.05 0.1
    str = Num.toStr dutyCycle
    result <- Task.attempt (Gpio.pwm frequency dutyCycle)
    when result is
        Ok {} -> Stdout.line "Duty cycle is: \(str).\n"
        Err PwmFailure -> Stdout.line "Failed to set PWM"

map = \value, inMin, inMax, outMin, outMax -> (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin

view this post on Zulip Nick Hallstrom (Feb 08 2023 at 06:09):

@Luke Boswell I think it downloads it quietly. Check your downloads.

view this post on Zulip Luke Boswell (Feb 08 2023 at 06:09):

That was it. Thanks

view this post on Zulip Brendan Hansknecht (Feb 08 2023 at 06:24):

Hurrah!!!

view this post on Zulip Brendan Hansknecht (Feb 08 2023 at 06:24):

Glad this is finally coming together

view this post on Zulip Nick Hallstrom (Feb 08 2023 at 06:26):

Ya it's been pretty satisfying to finally get things working :grinning_face_with_smiling_eyes: Next step is getting I2C working to communicate with the servo drivers which is going to be quite a bit more complicated than the above. Wish me luck!

view this post on Zulip Richard Feldman (Feb 08 2023 at 13:24):

this is so cool!!! :heart_eyes: :heart_eyes: :heart_eyes:

view this post on Zulip Richard Feldman (Feb 08 2023 at 13:33):

is it ok if I share it on Twitter?

view this post on Zulip Nick Hallstrom (Feb 08 2023 at 15:34):

Thanks :grinning_face_with_smiling_eyes: And of course! I’m @Billzabob_ on Twitter

view this post on Zulip Nick Hallstrom (Feb 10 2023 at 08:26):

Am I messing something up with the types here? In Rust I have:

pub extern "C" fn roc_fx_readByte(address: u16) -> RocResult<u8, ()>

In Effect I have:

readByte : U16 -> Effect (Result U8 {})

Then in I2c.roc I have:

readByte : U16 -> Task U8 [ReadFailure]
readByte = \address ->
    Effect.readByte address
    |> Effect.map (\result -> Result.mapErr result \{} -> ReadFailure)
    |> InternalTask.fromEffect

It thinks the Task is failing whenever I call it, even though in Rust I can verify that it's returning ok. If I change everything to not use RocResult and just change the type to:

pub extern "C" fn roc_fx_readByte(address: u16) -> u8 {

then it works just fine. It's driving me nuts not knowing what's going on here.

The repo is here if that helps.

view this post on Zulip Folkert de Vries (Feb 10 2023 at 12:58):

you'd have to look at the llvm code that roc actually produces to know what signature it expects

view this post on Zulip Folkert de Vries (Feb 10 2023 at 12:58):

it might for instance want the return value as a mutable reference that it writes the value into

view this post on Zulip Folkert de Vries (Feb 10 2023 at 12:58):

though that seems unlikely in this particular case

view this post on Zulip Nick Hallstrom (Feb 10 2023 at 14:53):

How would I do that? Or is this something I can use Roc Glue for?

view this post on Zulip Folkert de Vries (Feb 10 2023 at 14:58):

we have a --debug flag that will put a .ll file beside the generated app

view this post on Zulip Folkert de Vries (Feb 10 2023 at 14:58):

you can then search that file for your exposed function

view this post on Zulip Folkert de Vries (Feb 10 2023 at 14:59):

and if it's not immediately clear what the signature you find means, please post it here

view this post on Zulip Nick Hallstrom (Feb 10 2023 at 15:12):

I tried that and it said I need to install debugir. I tried doing that but it says I need LLVM 14 but I have LLVM 13 installed for compiling Roc from source. Do I need to upgrade LLVM?

view this post on Zulip Nick Hallstrom (Feb 10 2023 at 15:18):

Oh wait. Just checked out an old commit of DebugIr that was set to LLVM 13

view this post on Zulip Folkert de Vries (Feb 10 2023 at 15:23):

yes that should work

view this post on Zulip Nick Hallstrom (Feb 10 2023 at 15:27):

A737B97E-3C7D-479C-80CA-672770A6D962.jpg

view this post on Zulip Folkert de Vries (Feb 10 2023 at 15:28):

an unhelpful error

view this post on Zulip Folkert de Vries (Feb 10 2023 at 15:28):

I can run it locally in a couple of hours

view this post on Zulip Folkert de Vries (Feb 10 2023 at 15:28):

there are other ways to get that signature but they are harder to explain

view this post on Zulip Nick Hallstrom (Feb 10 2023 at 15:30):

That would be great! Thanks Folkert, really appreciate the help!

view this post on Zulip Nick Hallstrom (Feb 10 2023 at 17:17):

@Folkert de Vries I just realized that even though it's crashing with that error, I'm still getting the .ll file. Here's the output for readByte:
declare { [0 x i8], [1 x i8], i8, [0 x i8] } @roc_fx_readByte(i16) local_unnamed_addr

view this post on Zulip Folkert de Vries (Feb 10 2023 at 20:28):

well. this is interesting and frustrating: rust will send a RocResult<u8, ()> over to roc as a u16. we "receive" it as { [0 x i8], [1 x i8], i8, [0 x i8] }, which is reasonable (if you've seen LLVM IR before), but it is _not_ valid according to the C ABI

view this post on Zulip Folkert de Vries (Feb 10 2023 at 20:28):

my guess is that the signature is just UB and anything might happen from there

view this post on Zulip Folkert de Vries (Feb 10 2023 at 20:29):

so what we should do is implement more of the C ABI. For now, you can receive the bits on the roc end as an U16 and then you have to do some Num.shiftRightBy and Num.intCast to get the info you want

view this post on Zulip Folkert de Vries (Feb 10 2023 at 20:29):

that's the fastest way to get unstuck I think

view this post on Zulip Nick Hallstrom (Feb 11 2023 at 00:14):

Got the servo driver working! It was so satisfying and so much fun to get this working :grinning_face_with_smiling_eyes: Next step is to write up the 7-segment display code for them and then assemble the display.
Servo Driver Video

app "i2c"
    packages { pf: "../src/main.roc" }
    imports [pf.Gpio, pf.Stdout, pf.Stdin, pf.I2c, pf.Task.{ Task, await }]
    provides [main] to pf

address  = 0x0040
mode1    = 0
prescale = 254
sleepBit = 0x10

main =
    _ <- Task.attempt init
    Task.forever (Task.attempt run \_ -> Task.succeed {})

init =
    _ <- await (reset)
    # About 50 Hz
    setPrescale 121

run =
    _      <- await (Stdout.line "Enter the rotation amount (0 to 180):")
    amount <- await Stdin.line
    angle  <- await (Task.fromResult (Str.toF64 amount))
    _      <- await (setServoAngle 0 angle)
    _      <- await (setServoAngle 1 angle)
    _      <- await (setServoAngle 2 angle)
    Task.succeed {}

setServoAngle = \servo, angle ->
    register = 4 * servo + 8
    count = map angle 0 180 145 500 |> Num.floor
    low = Num.bitwiseAnd count 0xff |> Num.toU8
    high = Num.shiftRightZfBy count 8 |> Num.toU8
    _ <- await (writeRegister register low)
    _ <- await (writeRegister (register + 1) high)
    Task.succeed {}

setPrescale = \value ->
    _ <- await sleep
    _ <- await (writeRegister prescale value)
    _ <- await wakeup
    Gpio.sleep 5

readRegister = \register ->
    _     <- await (I2c.writeBytes address [register])
    bytes <- await (I2c.readBytes address 1)
    Task.fromResult (List.get bytes 0)

writeRegister = \register, value ->
    I2c.writeBytes address [register, value]

sleep =
    oldMode <- await (readRegister mode1)
    newMode = Num.bitwiseOr oldMode sleepBit
    writeRegister mode1 newMode

wakeup =
    oldMode <- await (readRegister mode1)
    newMode = sleepBit |> bitwiseNot |> Num.bitwiseAnd oldMode
    writeRegister mode1 newMode

# Surprised this isn't under Num already
bitwiseNot = \bits -> Num.bitwiseXor 0xff bits

reset = I2c.writeBytes 0 [0x06]

map = \value, inMin, inMax, outMin, outMax -> (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin

view this post on Zulip Brendan Hansknecht (Feb 11 2023 at 00:46):

It was so satisfying and so much fun to get this working

That's how I felt when messing with roc on a little arm microcontroller that was driving a robot.

view this post on Zulip Nick Hallstrom (Feb 11 2023 at 01:01):

Oh nice! What were you programming the robot to do?

view this post on Zulip Brendan Hansknecht (Feb 11 2023 at 05:05):

Most of the time was spent figuring out the python library and porting it to rust. But once that was done enough, just a number basic tasks with light sensors and encoders. So nothing too interesting was mostly just a test bed

view this post on Zulip Nick Hallstrom (Feb 11 2023 at 05:07):

Haha ya I get it though. Trying to explain to my friend why I was so excited about a blinking light was interesting :joy:

view this post on Zulip Notification Bot (Feb 11 2023 at 15:50):

11 messages were moved from this topic to #off-topic > FPGA side project by Brendan Hansknecht.

view this post on Zulip Nick Hallstrom (Feb 15 2023 at 07:33):

Some good progress with the Roc Clock! NUMBERS! :grinning_face_with_smiling_eyes:
Counting Digits

view this post on Zulip Anton (Feb 15 2023 at 08:51):

That's not the kind of clock I was expecting but it looks super cool :)

view this post on Zulip Nick Hallstrom (Feb 15 2023 at 11:42):

Haha thanks! It’s also much louder than you’re average clock 😅 I’m trying to decide where I can put it where the sound of servos running in unison every minute won’t drive me insane 😂

view this post on Zulip Nick Hallstrom (Feb 15 2023 at 18:51):

Ok, last time, I'll stop spamming videos after this. The Roc Clock is pretty much complete! :grinning_face_with_smiling_eyes: I just need to hook it up to the actual time now, but it's suuuper fun to play with.
The code can be found here
Full Clock


Last updated: Jun 16 2026 at 16:19 UTC