6 ms·
How to blink a light in 10 different programming languages
- eutectic 11y agoAnd in Haskell (feel free to include this): import Control.Concurrent (threadDelay) import System.IO main :: IO () main = withFile "/sys/class/leds/beaglebone:green:usr0/brightness" AppendMode go where put h s = hPutStrLn h s >> threadDelay 2000000 go h = let m = put h "0" >> put h "1" >> m in m
- codygman 11y agoWhy: go h = let m = put h "0" >> put h "1" >> m in m Rather than: go h = put h "0" >> put h "1" Oh, recursion...
- mcconnma 11y agoThanks, any thoughts on why I get this? $ ghc -o blink blink.hs [1 of 1] Compiling Main ( blink.hs, blink.o ) blink.hs:7:22: parse error on input `='
- eutectic 11y agoNot sure; I copy-pasted it back from hackernews and it works fine for me.
- gergoerdi 11y agoAs always, prefer prebuilt recursion scheme combinators instead of making your own. The following, using `forever`, is IMO much more readable because you don't have to mentally resolve the recursion: main :: IO () main = withFile ledFile AppendMode $ \h -> do let put s = hPutStrLn h s >> threadDelay 2000000 forever $ do put "0" put "1" where ledFile = "/sys/class/leds/beaglebone:green:usr0/brightness"
- valleyer 11y agoFirst example seems like it would want >, not <
- mcconnma 11y agoyou're right,fixed.
- escaped_hn 11y agoHow to open a file in 10 different programming languages.
- asadhaider 11y agoYeah this exactly, I was disappointed reading the article.
- mcconnma 11y agoIt is a little more than that as you also need to write alternating state to the file, but you are right, and that is the point of the post. You can read/write embedded sensors/actuators in whatever language you are comfortable by simply reading/writing from the Linux file system.
- joezydeco 11y ago...if your Linux base port has the chip-specific GPIO driver wired up correctly and is enabled in the kernel along with the sysfs interface.
- pjc50 11y ago.. on a correctly set up beaglebone, not other systems. It would be much better if this were in the title. On most embedded systems and FPGAs faffing with the toolchain and hunting in the documentation is 99% of the effort of getting an LED blinking, that's why it's used as the embedded 'hello world'.
- mcconnma 11y agogood point, I updated the front matter of the post to make this more explicit.
- swiley 11y agoOn Linux you're allowed to mmap gpio as well if I'm not mistaken.
- 11y ago
- deleted 11y ago[deleted]
- z3t4 11y agoI find using name-spaces in imports makes the code a hundred times easier to reason about.
- z3t4 11y agoWith JavaScript (nodejs) var lightsOn = true; setInterval(blink, 2000); function blink() { var fs = require("fs"); fs.writeFile("/sys/class/leds/beaglebone:green:usr0/brightness", lightsOn, function (err) { if (err) throw err; }); lightsOn = lightsOn ? false : true; }
- have_faith 11y agoIt makes no difference but for some reason I feel the urge to shorten the last line to: lightsOn = !lightsOn; I'll see myself out.
- polishninja 11y agoDon't forget to turn off the light on your way out. lightsOn = !lightsOn || lightsOn
- Cshelton 11y agoEvery time I write that in JavaScript, I delete it and just use ! As well haha
- mcconnma 11y agoWriting a boolean type to SysFS/GPIO from NodeJS doesn't quite work so I tweaked your example to following similar pattern of the other languages. Thanks.
- z3t4 11y agoI wanted to show the power of JavaScript types, and you got a type bug? What the irony. I'll probably report that as a bug in nodejs or an undocumented "feature".
- userbinator 11y agoYou may "How to blink a light extremely inefficiently 10 different ways." ;-) Seriously, not even one Asm example? If you have a PC that has a parallel port and can boot to DOS, you can stick a LED in series with a 100-ohm resistor across pins 2(+) and 18(-) and do this: mov dx, 378h xor ax, ax blinkloop: out dx, al mov cx, 36 call delay not ax jmp blinkloop delay: hlt loop delay ret I find it somewhat amazing that this program, in Assembly, is actually shorter than the 10 HLL programs in the article.
- tomcam 11y agouserbinator's example does not conform to the API, which writes to a file (and on some platforms, uses best practices to write atomically and check for errors to boot). I took a stab at it elsewhere.
- tomcam 11y agoHere's something more appropriate (air coded, modulo my iPhone's spell corrector) section .text global _start _start: ; Write the ‘1’ to a file mov eax, 4 ; sys_write mov edx, 1 ; # of bytes to write mov ecx, ‘1’ ; Means light should be on mov ebx, file_open ; File descriptor mov eax, 4 ; Call to sys_write int 0x80 ; Call kernel to write file ; Close file move eax, 6 mov mov ebx, int 0x80 ; Call kernel to close file ; Quit to OS move eax, 1 ; SYS_EXIT int 0x80 ; Call kernel to exit to OS section.data filename db ‘/sys/class/leds/beaglebone:green:usr0/brightness’,0 len equ $-filename section .bss file_open resb 1 state db ‘1’
- kyberias 11y agoThat's not a fair comparison in any way. Your assembler program doesn't do half of the original code.
- userbinator 11y agoThat's the point. It doesn't do all the work of the original code because it doesn't need to. You don't need a whole OS and several layers of abstraction just to blink an LED, not even a dozen instructions (and I notice now, since I was writing it off the top of my head, that the delay subroutine is only called once, so there's another two "wasted" instructions...) I think it's very important that programmers are exposed to such "absolute minimum simplicity" solutions, as it helps greatly with understanding the essence of the problem and its solution - in this case it's nothing more than toggling a bit in an I/O port. Complexity can be added afterwards as needed, but not gratuitously. I like how one of the other comments here and in the article mentions that there's no error handling in many of the examples, since it brings up another point about complexity: it can introduce opportunities for errors. If you're working with the GPIO directly, errors either can't occur, or they're hardware-level (e.g. shorted output) and nothing can really be done about it anyway unless the hardware has additional features for signaling such conditions. If you're going through the OS' filesystem, errors can occur at any one of the multiple layers for not-so-related reasons to the actual task. One should then ask the question of whether the code that's added to handle errors is actually doing something useful for the task, or serves no purpose beyond satisfying the other layers of complexity that introduce them.
- miguelrochefort 11y agoC# https://dotnetfiddle.net/afyTvh https://dotnetfiddle.net/afyTvh using System; using System.IO; using System.Threading; public class Blink { const string FilePath = @"/sys/class/leds/beaglebone\:green\:usr0/brightness"; const int BlinkDelay = 2000; public static void Main() { using (var writer = new StreamWriter(FilePath)) { for (var state = true; true; state = !state) { writer.Write((state ? 1 : 0).ToString("00")); Thread.Sleep(2000); } } } } Reactive Extensions (C#) https://dotnetfiddle.net/bpOF5e https://dotnetfiddle.net/bpOF5e Observable .Repeat(Observable.Unit) .Select((index, _) => index % 2) .Select(string.Format({0:00}) .Delay(TimeSpan.FromSeconds(2)) .Aggregate( new StreamWriter(@"/sys/class/leds/beaglebone\:green\:usr0/brightness"), writer => (writer, state) => writer.Write(state) ) .Subscribe(writer => writer.Close());
- userbinator 11y agoBoth your example and the Java one are not abstract enough (although the added string conversion is nice.) I was expecting more design patterns and layering for a truly enterprise-quality solution, like this: https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...
- mcconnma 11y agoThis is my favorite comment :-) After years of programming in enterprise Java, I failed to truly make it enterprise worthy! I'm going to see if I can get an assembly example added per your other comment.
- deleted 11y ago[deleted]
- icefox 11y agoThe bash example should have set -e or #!/bin/bash -e so that it will exit on error. That or the rest of the languages also shouldn't bother to check for errors. Also rather than invoking another shell use let and do let STATE=1-$STATE just like all the other programs. And why does bash need to have comments. Maybe it was a subtle joke that C doesn't get comments, but bash does :D in which case well played. #!/bin/bash -e STATE=1 while : do echo $STATE > /sys/class/leds/beaglebone\:green\:usr0/brightness let STATE=1-$STATE sleep 2 done While all of the programs can be made smaller in size the bash script is probably the default way that the script would be tossed together. Bash has a ton of problems, but sadly between it and the standard unix commands you can get the job done too quickly and easily.
- pdkl95 11y ago> invoking another shell That wasn't a subshell - $(( )) is Arithmetic Expansion. (you may be thinking of $( ) for Command Substitution) $ echo $BASH_SUBSHELL $((BASH_SUBSHELL)) $(echo $BASH_SUBSHELL) 0 0 1 Now, using the result of a boolean is a bit strange; I would have used your math with the modern expansion style: # the new style allows spaces STATE=$(( 1 - $STATE ))
- icefox 11y agoAh, thats great learned a new bash trick
- barrkel 11y agoShorthand for $(( )) is $[ ] In predicate contexts (which means return code in bash) like if, while or the left hand side of '&&', consider using (( )) - this has an exit code of 0 if the arithmetic expression is true (non-zero) and 1 otherwise. Use it like this: let x=3*3 if (( x > 5 )); then echo greater than five else echo less than or equal to five end
- Sanddancer 11y agoI am disappointed. I opened this article expecting a myriad of MOVs and JMPs, PEEKS and POKEs, &s and *s, <s and +s, DOs and GO TOs. These are all languages that borrow a lot from each other, and as such look mostly the same. We need some variety in here, some zing. Give some FORTRAN IV, some COBOL, some BASIC even, and it could be interesting, especially if you add in how to do it with raw io; sysfs is ugly and boring.
- mcconnma 11y agoIt wasn't clear in the original post but this was really meant for embedded Linux platforms that already have GPIO mapped via SysFS so assembly wasn't considered. I've updated the front matter of the post to reflect that. I don't know if Pascal is considered 'zing' these days, but I added that as an example. Thanks for your comment.
- ahoge 11y agoDart: import 'dart:async'; import 'dart:io'; main() { var file = new File('/sys/class/leds/beaglebone:green:usr0/brightness'); var state = 0; new Timer.periodic(new Duration(seconds: 2), (_) { state = 1 - state; file.writeAsStringSync('0$state', flush: true); }); } While it's not the most concise version, it's certainly very readable. Everyone can clearly see that the duration is 2 seconds and that that boolean flag is for flushing, because that's exactly what the code says.
- ju-st 11y agoYes, I like it! Only thing I don't understand is '0$state'.
- ahoge 11y agoThat's string interpolation. That line writes either "00" or "01". It's essentially the same as `'0' + state`. 'x: $x, y: $y' is a lot easier to type than: 'x: ' + x + ', y: ' + y I actually had to triple check that line. Concatenation is very error-prone. I often mess it up. String interpolation is a fairly popular feature nowadays: https://en.wikipedia.org/wiki/String_interpolation https://en.wikipedia.org/wiki/String_interpolation
- blcknight 11y agoRuby example is not very idiomatic. Give File.open a block and it'll handle closing it. Instead of flushing the file every time, you could just set file.sync = true once. Also, if one was going to use the ensure block, use nil? not '== nil'
- ripa 11y agoSimilar quirks in the Python example. Opening the file should ideally be done with a "with" statement to close the file automatically at the end. But in the Ruby example the author at least closed the file instead of doing nothing as in the Python example. :)
- asciimike 11y agoBonescript is pretty simple as well: // npm install bonescript var b = require('bonescript'); b.pinMode('P8_13', b.OUTPUT); b.digitalWrite('P8_13', 1); Pretty sure Bonescript is just doing the filesystem stuff under the hood, though doing memory mapping would be way nicer.
- kyberias 11y agoThe Ruby example doesn't handle exceptions with the same granularity as Java example and is therefore much shorter. Please do not compare the examples.
- mbanzi 11y agoAlternatively one could use the Arduino API on the beaglebone with https://github.com/prpplague/Userspace-Arduino https://github.com/prpplague/Userspace-Arduino It would make the whole thing less scary for beginners and there are tons of examples and documentation out there.
- mcconnma 11y agothat is interesting, not sure if it is any easier than compiling some of the examples I provide, but it does open Linux platforms to those that know wiring, thanks.
- AirMarshalHenry 11y agoA bit more compact go-version: package main import ( "log" "os" "strconv" "time" ) func main() { f, err := os.OpenFile("/sys/class/leds/beaglebone:green:usr0/brightness", os.O_RDWR, 0666) if err != nil { log.Fatal(err) } defer f.Close() state := 1 for i := 0; i < 3*2; i++ { _, err = f.WriteString(strconv.Itoa(state)) if err != nil { log.Fatal(err) } time.Sleep(2 * time.Second) state = 1 - state } }
- mcconnma 11y agoThanks, yeah, I went crazy and implemented it via go channels, which isn't a fair comparison. I updated with a slightly tweaked version of what you provided.