mirror of
https://github.com/TomHodson/tomhodson.github.com.git
synced 2025-06-26 10:01:18 +02:00
updates
This commit is contained in:
parent
dd66d95a36
commit
52abcce648
1
Gemfile
1
Gemfile
@ -7,3 +7,4 @@ gem "jekyll"
|
|||||||
gem "webrick", "~> 1.7"
|
gem "webrick", "~> 1.7"
|
||||||
gem 'jekyll-feed'
|
gem 'jekyll-feed'
|
||||||
gem 'jekyll-redirect-from'
|
gem 'jekyll-redirect-from'
|
||||||
|
gem 'jekyll_flexible_include'
|
||||||
|
@ -38,6 +38,13 @@ GEM
|
|||||||
sassc (> 2.0.1, < 3.0)
|
sassc (> 2.0.1, < 3.0)
|
||||||
jekyll-watch (2.2.1)
|
jekyll-watch (2.2.1)
|
||||||
listen (~> 3.0)
|
listen (~> 3.0)
|
||||||
|
jekyll_flexible_include (2.0.14)
|
||||||
|
jekyll (>= 3.5.0)
|
||||||
|
jekyll_plugin_logger (~> 2.1.0)
|
||||||
|
key-value-parser
|
||||||
|
jekyll_plugin_logger (2.1.1)
|
||||||
|
jekyll (>= 3.5.0)
|
||||||
|
key-value-parser (0.0.2)
|
||||||
kramdown (2.4.0)
|
kramdown (2.4.0)
|
||||||
rexml
|
rexml
|
||||||
kramdown-parser-gfm (1.1.0)
|
kramdown-parser-gfm (1.1.0)
|
||||||
@ -70,6 +77,7 @@ DEPENDENCIES
|
|||||||
jekyll
|
jekyll
|
||||||
jekyll-feed
|
jekyll-feed
|
||||||
jekyll-redirect-from
|
jekyll-redirect-from
|
||||||
|
jekyll_flexible_include
|
||||||
webrick (~> 1.7)
|
webrick (~> 1.7)
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
|
@ -19,10 +19,12 @@ collections:
|
|||||||
plugins:
|
plugins:
|
||||||
- jekyll-feed
|
- jekyll-feed
|
||||||
- jekyll-redirect-from
|
- jekyll-redirect-from
|
||||||
|
- flexible_include
|
||||||
|
|
||||||
whitelist:
|
whitelist:
|
||||||
- jekyll-feed
|
- jekyll-feed
|
||||||
- jekyll-redirect-from
|
- jekyll-redirect-from
|
||||||
|
- flexible_include
|
||||||
|
|
||||||
feed:
|
feed:
|
||||||
posts_limit: 20
|
posts_limit: 20
|
143
_posts/2022-11-23-emscripten_arduino.md
Normal file
143
_posts/2022-11-23-emscripten_arduino.md
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
---
|
||||||
|
title: Using emscripten to simulate an arduino project
|
||||||
|
layout: post
|
||||||
|
image: /assets/blog/emscripten_arduino/arduino.svg
|
||||||
|
---
|
||||||
|
|
||||||
|
In [another post](/2022/11/22/sensor_watch.html) I talked about the [Sensor Watch](https://www.oddlyspecificobjects.com/products/sensorwatch/) project which has this nifty JS simulation for testing the firmware. This saves you having to dissasemble the watch every time you want to test a change to the firmware so makes the develop/test loop much faster. Here I'll go through a toy example of how that works.
|
||||||
|
|
||||||
|
If you're working on a project where much of the work consists of configuring the hardware correctly then the effort of making a simulation like this is probably not worth it. For projects like the sensor watch, however, where the inputs and outputs are pretty much fixed while lots of people will want to modify the software it makes a lot of sense.
|
||||||
|
|
||||||
|
The sensor watch project has a firmware with a clearly defined interface and the trick is that you swap out the implementation of this interface between the real hardware and the simulation. I wanted to get a better understanding of this so I thought I'd so a super simple version myself, let's do that classic arduino project... blinky!
|
||||||
|
|
||||||
|
Let's grab [the code](https://docs.arduino.cc/built-in-examples/basics/Blink) for blinky.ino, we could easily compile this for a real arduino using the IDE or using a makefile. I'm gonna skip the details of getting this working for both real hardware and for emscripten to keep it simple.[^1] The starting point is to try to compile a simple arduino sketch like this one:
|
||||||
|
```c
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.println("Setting pinMode of pin 13");
|
||||||
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
Serial.println("LED On");
|
||||||
|
digitalWrite(LED_BUILTIN, HIGH);
|
||||||
|
delay(1000);
|
||||||
|
Serial.println("LED Off");
|
||||||
|
digitalWrite(LED_BUILTIN, LOW);
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to get this to compile using emscripten we need to do two things:
|
||||||
|
1. write a wrapper script that runs `setup()` and then calls `loop()` in an infinite loop
|
||||||
|
2. provide implementations of functions like `digitalWrite` and `delay` that interface with emscripten.
|
||||||
|
|
||||||
|
The first bit is pretty easy:
|
||||||
|
```c
|
||||||
|
#include "blink.c"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
setup();
|
||||||
|
while(1) loop();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
It's not typical to include a .c file like this. To avoid having to recompile everything all the time, you're really supposed to just include the .h file, compile .c files to .o object files and then link them all together at the end. For this small example it's fine though.
|
||||||
|
|
||||||
|
## Writing Arduino.h
|
||||||
|
|
||||||
|
Ok, now let's write a quick implementation of `Arduino.h` that just covers what we need for this example, I'll include the code in the header file for brevity. First we have some includes. We need `stdint` for the fixed width integer types like `uint8_t`, stdio for `printf` which emscripten provides its own implementation of and `<emscripten.h>` to embed javascript implementations. We also have some simple definitions that appear in our sketch.
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <emscripten.h>
|
||||||
|
#include <emscripten/html5.h>
|
||||||
|
|
||||||
|
#define HIGH 1
|
||||||
|
#define LOW 0
|
||||||
|
#define LED_BUILTIN 13
|
||||||
|
#define OUTPUT 1
|
||||||
|
#define INPUT 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Next we do `digitalWrite`. We use the `EM_ASM` macro which just pastes that JS code into the compiled wasm, substiting in the `value` for `$0`. I grabbed a creative commons licensed [svg](https://commons.wikimedia.org/wiki/File:Led_lampeggiante_con_arduino.svg) off wikipedia, added a little overlay in inkscape with the id `light_led` and then we toggle it's opacity.
|
||||||
|
|
||||||
|
```c
|
||||||
|
void pinMode(uint8_t pin, uint8_t value) {}
|
||||||
|
void digitalWrite(uint8_t pin, uint8_t value) {
|
||||||
|
if(pin == 13) {
|
||||||
|
EM_ASM({
|
||||||
|
document.getElementById("light_led").style.opacity = $0 ? 1 : 0;
|
||||||
|
}, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For `delay`, it's a bit more complicated because the JS that runs in browsers has to share the CPU, it can't delay by just wasting cycles the way we can on an arduino. So we use the [asyncify](https://emscripten.org/docs/porting/asyncify.html) feature of emscripten. This gives us a function `emscripten_sleep` that effectively yields to other code running on the page for a certain period of time, this allows us to implement `delay` in a non-blocking way.
|
||||||
|
|
||||||
|
```c
|
||||||
|
void delay(uint32_t milliseconds) {
|
||||||
|
emscripten_sleep(milliseconds);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, `Serial.println` should be pretty easy, we just call `printf`. However we need to do something to mimic to the `Serial.print` syntax which involves a little C++:
|
||||||
|
```c
|
||||||
|
|
||||||
|
class SerialClass {
|
||||||
|
public:
|
||||||
|
void begin(uint32_t baud) {}
|
||||||
|
void println(const char string[]) {
|
||||||
|
printf("%s\n", string);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SerialClass Serial;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compiling it
|
||||||
|
|
||||||
|
And with that we're almost done! We have three files, `blink.c` that represents our arduino sketch, `main.c` that wraps it and `Arduino.h` that implements the lower level stuff. To compile it we need the emscripten C++ compiler `em++`
|
||||||
|
```bash
|
||||||
|
em++ -sASYNCIFY -O3 main.c -o build/main.js -I./
|
||||||
|
```
|
||||||
|
`-sASYNCIFY` tells emscripten that it should us asyncify, `-O3` runs optimisations as recommended when using asyncify and `-I./` tells the compiler that `Arduino.h` can be found in the same directory as `main.c`. We get two files as output `main.js` and `main.wasm`. The former is another wrapper script and `main.wasm` contains the actual compiled webassembly code.
|
||||||
|
|
||||||
|
## Another Wrapper to wrap it up
|
||||||
|
So how do we use `main.js` and `main.wasm`? We need to include `main.js` and some extra glue code `loader.js` on our HTML page, along with our SVG and a textarea tag that the serial output will go to:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<figure>
|
||||||
|
<svg>...</svg>
|
||||||
|
Serial Console
|
||||||
|
<textarea class="emscripten" id="output" rows="8"
|
||||||
|
style="width: 80%; display: block; margin: 1em;"></textarea>
|
||||||
|
<figcaption>
|
||||||
|
The finished arduino simulation.
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<script async type="text/javascript" src="loader.js"></script>
|
||||||
|
<script async type="text/javascript" src="main.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
And that gives us the final result:
|
||||||
|
<figure>
|
||||||
|
{% flexible_include assets/blog/emscripten_arduino/arduino.svg do_not_escape %}
|
||||||
|
Serial Console
|
||||||
|
<textarea class="emscripten" id="output" rows="8" style="width: 80%; display: block; margin: 1em;"></textarea>
|
||||||
|
<figcaption>
|
||||||
|
The finished arduino simulation.
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<script async type="text/javascript" src="/assets/blog/emscripten_arduino/loader.js"></script>
|
||||||
|
<script async type="text/javascript" src="/assets/blog/emscripten_arduino/main.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[^1]: For a real project it'd be nice to integrate emscripten with a makefile that can compile for real hardware [this one](https://github.com/sudar/Arduino-Makefile)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
|||||||
---
|
|
||||||
title: Using emscripten to simulate an arduino project
|
|
||||||
layout: post
|
|
||||||
image: /assets/blog/emscripten_arduino/arduino.svg
|
|
||||||
---
|
|
||||||
|
|
||||||
NB: try to copy the approach from here
|
|
||||||
|
|
||||||
In [another post](/2022/11/22/sensor_watch.html) I talked about the [Sensor Watch](https://www.oddlyspecificobjects.com/products/sensorwatch/) project which has this nifty JS simulation for testing the firmware. This saves you having to dissasemble the watch every time you want to test a change to the firmware so makes the develop/test loop much faster.
|
|
||||||
|
|
||||||
If you're working on a project where much of the work consists of configuring the hardware correctly then the effort of making a simulation like this is probably not worth it. For projects like the sensor watch, however, where the inputs and outputs are pretty much fixed while lots of people will want to modify the software it makes a lot of sense.
|
|
||||||
|
|
||||||
The sensor watch project has a firmware with a clearly defined interface and the trick is that you swap out the implementation of this interface between the real hardware and the simulation. I wanted to get a better understanding of this so I thought I'd so a super simple version myself, let's do that classic arduino project... blinky!
|
|
||||||
|
|
||||||
Let's grab [the code](https://docs.arduino.cc/built-in-examples/basics/Blink) for blinky.ino, we could easily compile this for a real arduino using the IDE or using a makefile. The approach that
|
|
||||||
```c
|
|
||||||
void setup() {
|
|
||||||
pinMode(LED_BUILTIN, OUTPUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
digitalWrite(LED_BUILTIN, HIGH);
|
|
||||||
delay(1000);
|
|
||||||
digitalWrite(LED_BUILTIN, LOW);
|
|
||||||
delay(1000);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
I'm going to use the makefile approach because emscripten knows how to work with makefiles. I installed [this one](https://github.com/sudar/Arduino-Makefile) and wrote a small child makefile:
|
|
||||||
```make
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -76,9 +76,6 @@ a:hover {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.figure {
|
.figure {
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
|
@ -13,11 +13,12 @@ figure {
|
|||||||
// border-top: solid #222 1px;
|
// border-top: solid #222 1px;
|
||||||
// padding-top: 1em;
|
// padding-top: 1em;
|
||||||
}
|
}
|
||||||
figure img {
|
figure img,svg {
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
width: 100%;
|
width: 80%;
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
figcaption {
|
figcaption {
|
||||||
aria-hidden: true;
|
aria-hidden: true;
|
||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 388 KiB After Width: | Height: | Size: 660 KiB |
20
assets/blog/emscripten_arduino/loader.js
Normal file
20
assets/blog/emscripten_arduino/loader.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
var statusElement = document.getElementById('status');
|
||||||
|
var progressElement = document.getElementById('progress');
|
||||||
|
var spinnerElement = document.getElementById('spinner');
|
||||||
|
|
||||||
|
var Module = {
|
||||||
|
preRun: [],
|
||||||
|
postRun: [],
|
||||||
|
print: (function() {
|
||||||
|
var element = document.getElementById('output');
|
||||||
|
if (element) element.value = ''; // clear browser cache
|
||||||
|
return function(text) {
|
||||||
|
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||||
|
console.log(text);
|
||||||
|
if (element) {
|
||||||
|
element.value += text + "\n";
|
||||||
|
element.scrollTop = element.scrollHeight; // focus on bottom
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
};
|
2371
assets/blog/emscripten_arduino/main.js
Normal file
2371
assets/blog/emscripten_arduino/main.js
Normal file
File diff suppressed because it is too large
Load Diff
BIN
assets/blog/emscripten_arduino/main.wasm
Executable file
BIN
assets/blog/emscripten_arduino/main.wasm
Executable file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user