From d2f91fec0448204c1cf53230ff51a7df4bc5e6fe Mon Sep 17 00:00:00 2001 From: Jon Froehlich Date: Tue, 23 Jun 2026 17:07:27 -0700 Subject: [PATCH] Standardize code blocks on fenced ```lang; add markdownlint CI gate (#99) Migrate every Jekyll `{% highlight %}` block to a fenced code block with a required language token, and enforce the convention in CI. Content: - Convert all `{% highlight LANG %}`...`{% endhighlight %}` to fenced ``` blocks across 35 lesson/doc pages. Normalize the language token to lowercase and use `cpp` for all Arduino/ESP32 sketches (was `C`/`C++`); JavaScript->javascript, HTML->html, CSS->css, etc. - Give every previously-bare fenced block a language: `text` for terminal output / program output / file trees / format examples, `bash` for shell commands. Satisfies MD040 site-wide. - Fix two commented-out example blocks whose opener migrated but whose inline closer (`... %} -->`, `{% endraw %}{% endhighlight %}`) did not, which left unbalanced Liquid tags; the LaTeX doc example is now correctly wrapped in raw/endraw so it renders literally. Gate: - Add .markdownlint-code.jsonc: a scoped config (MD040 language-required, MD046 fenced-not-indented, MD048 backtick fences) kept separate from the editor .markdownlint.jsonc so it only checks code-block rules. - Add a `code-blocks` job to content-lint.yml running `markdownlint-cli -c` (replaces config; cli2 would merge the editor config back in). - Document the convention and gate in website-dev.md "Code highlighting". Rationale: portability + tooling, and de-risking a future framework migration (#103) where Liquid tags would all need rewriting. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/content-lint.yml | 24 ++++- .markdownlint-code.jsonc | 24 +++++ advancedio/addressable-leds.md | 32 +++--- advancedio/oled.md | 62 ++++++------ advancedio/servo.md | 20 ++-- advancedio/smoothing-input.md | 8 +- advancedio/vibromotor.md | 12 +-- arduino/buttons.md | 8 +- arduino/debouncing.md | 8 +- arduino/fast-analog-read.md | 4 +- arduino/force-sensitive-resistors.md | 4 +- arduino/inside-arduino.md | 30 +++--- arduino/led-blink.md | 24 ++--- arduino/led-blink2.md | 8 +- arduino/led-blink3.md | 34 +++---- arduino/led-fade.md | 12 +-- arduino/piano.md | 16 +-- arduino/potentiometers-old.md | 4 +- arduino/potentiometers.md | 8 +- arduino/rgb-led-fade.md | 22 ++--- arduino/rgb-led.md | 28 +++--- arduino/serial-print.md | 34 +++---- arduino/tone.md | 32 +++--- communication/handpose-serial.md | 64 ++++++------ communication/ml5js-serial.md | 48 ++++----- communication/p5js-paint-io.md | 90 ++++++++--------- communication/p5js-serial-io.md | 140 +++++++++++++-------------- communication/p5js-serial.md | 64 ++++++------ communication/serial-intro.md | 89 ++++++++--------- communication/web-serial.md | 92 +++++++++--------- esp32/esp32-ide.md | 4 +- esp32/led-fade.md | 2 +- sensors/photoresistors.md | 8 +- signals/index.md | 2 +- signals/jupyter-notebook.md | 4 +- website-dev.md | 95 ++++++++++++------ website-install.md | 36 +++---- 37 files changed, 639 insertions(+), 557 deletions(-) create mode 100644 .markdownlint-code.jsonc diff --git a/.github/workflows/content-lint.yml b/.github/workflows/content-lint.yml index 865e4811..87d7c82c 100644 --- a/.github/workflows/content-lint.yml +++ b/.github/workflows/content-lint.yml @@ -3,7 +3,8 @@ # Runs lightweight checks on the Markdown content. Separate from the deploy # workflow (jekyll.yml) on purpose: a content-convention miss should block a # MERGE, but never take down the live site. This is also the home for future -# content gates (e.g. the #99 code-block standardization lint). +# content gates. Current gates: SEO front matter, media a11y, code-block +# standardization (#99), and html-proofer link/anchor/HTML validation. name: Content lint on: @@ -45,6 +46,27 @@ jobs: - name: Check media a11y (iframe title, video aria-label, image alt) run: python scripts/check_a11y.py --ci + code-blocks: + # Enforce #99 code-block standardization on the Markdown SOURCE: every fenced + # block declares a language (MD040), blocks are fenced not indented (MD046), + # and fences use backticks (MD048). Uses markdownlint-cli (NOT cli2) with -c + # so the scoped .markdownlint-code.jsonc fully replaces config instead of + # merging the editor .markdownlint.jsonc back in. House style: ```cpp for all + # Arduino/ESP32 sketches. Deliberate indented demos opt out inline with + # . + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Lint code-block conventions (MD040/MD046/MD048) + run: npx --yes markdownlint-cli@0.48.0 -c .markdownlint-code.jsonc "**/*.md" --ignore "_site" --ignore "node_modules" + link-check: # Validate the BUILT site with html-proofer: broken internal links, broken # anchors, missing image alt, and malformed HTML. External links are NOT diff --git a/.markdownlint-code.jsonc b/.markdownlint-code.jsonc new file mode 100644 index 00000000..d9128611 --- /dev/null +++ b/.markdownlint-code.jsonc @@ -0,0 +1,24 @@ +{ + // Scoped markdownlint config for the CI "code-blocks" gate (issue #99). + // + // This is INTENTIONALLY separate from the editor config (.markdownlint.jsonc): + // it checks ONLY the code-block standardization rules so the gate can't fail + // on the many unrelated style nits (MD009/MD022/MD032/...) that the content + // predates. The CI job runs it via `markdownlint-cli -c` (which fully replaces + // config, rather than markdownlint-cli2 which merges the discovered editor + // config back in). Filename is non-standard on purpose so no tool auto-loads + // it for editing. + "default": false, + + // MD040: every fenced code block must declare a language token (e.g. ```cpp, + // ```bash, ```text). cpp is the house style for all Arduino/ESP32 sketches. + "MD040": true, + + // MD046: code blocks must be fenced, never indented (an indented block can't + // carry a language token, so it would silently evade MD040). Deliberate + // indented demos opt out inline with . + "MD046": { "style": "fenced" }, + + // MD048: fences use backticks (```), not tildes (~~~), for consistency. + "MD048": { "style": "backtick" } +} diff --git a/advancedio/addressable-leds.md b/advancedio/addressable-leds.md index e5bffc00..e496ce05 100644 --- a/advancedio/addressable-leds.md +++ b/advancedio/addressable-leds.md @@ -203,7 +203,7 @@ The Adafruit NeoPixel library can be installed directly from the Arduino Library The NeoPixel library API will feel familiar if you've completed the [OLED lesson](oled.md)—it follows the same **buffer → display** pattern: -{% highlight C++ %} +```cpp #include const int LED_PIN = 2; // Any digital pin works — no PWM required! @@ -219,7 +219,7 @@ void setup() { strip.setBrightness(50); // Set brightness (0-255). 50 is ~20% bright strip.show(); // Initialize all pixels to 'off' } -{% endhighlight C++ %} +``` {: .note } > Notice the same **buffer → show** pattern from the [OLED lesson](oled.md): `setPixelColor()` writes to a buffer in RAM, and `show()` pushes the data to the LEDs. If you forget to call `show()`, nothing will change on the LEDs—just like forgetting `_display.display()` on the OLED! @@ -247,7 +247,7 @@ Here are the most commonly used functions from the [Adafruit NeoPixel library](h Each pixel's color is specified using RGB values from an 8-bit value—0-255 per channel—just like the [RGB LED lesson](../arduino/rgb-led.md). Some examples: -{% highlight C++ %} +```cpp // Named colors using strip.Color(R, G, B) uint32_t red = strip.Color(255, 0, 0); uint32_t green = strip.Color(0, 255, 0); @@ -255,11 +255,11 @@ uint32_t blue = strip.Color(0, 0, 255); uint32_t white = strip.Color(255, 255, 255); uint32_t purple = strip.Color(128, 0, 255); uint32_t off = strip.Color(0, 0, 0); -{% endhighlight C++ %} +``` For animations that cycle through colors, the **HSV** (hue, saturation, value) color space is much more useful than RGB. Remember the [HSL crossfading lesson](../arduino/rgb-led-fade.md)? The same principle applies here. The `ColorHSV()` function lets you smoothly sweep through the entire rainbow by varying just the hue value: -{% highlight C++ %} +```cpp // Hue ranges from 0 to 65535 (full color wheel) // 0 = red, ~10922 = yellow, ~21845 = green, ~32768 = cyan, // ~43690 = blue, ~54613 = magenta, 65535 wraps back to red @@ -269,7 +269,7 @@ uint32_t color = strip.ColorHSV(hue, 255, 255); // Full sat, full brightness // Note: ColorHSV returns a value that should be passed through strip.gamma32() // for perceptually accurate colors: strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(hue, 255, 255))); -{% endhighlight C++ %} +``` {: .note } > **What is `gamma32()`?** Human eyes perceive brightness non-linearly—the difference between 0 and 50 looks much bigger than the difference between 200 and 250. The `gamma32()` function applies a correction curve so that color transitions look smooth and natural to our eyes. It's optional but makes a noticeable difference in gradients and fades. @@ -381,7 +381,7 @@ Now that we understand how addressable LEDs work and have our stick wired up, le Let's start by simply setting each LED to a different color. This confirms that your wiring is correct and that the library is communicating with all 8 LEDs. This is our equivalent of the [shape drawing activity](oled.md#activity-draw-shapes-and-text) from the OLED lesson—the simplest possible test. -{% highlight C++ %} +```cpp #include const int LED_PIN = 2; @@ -409,7 +409,7 @@ void setup() { void loop() { // Nothing to do — the colors persist until changed } -{% endhighlight C++ %} +``` If your colors look wrong (*e.g.,* you asked for red but got green), try changing `NEO_GRB` to `NEO_RGB` in the strip constructor. This is the most common issue students encounter! @@ -423,7 +423,7 @@ Try playing with the colors by changing the RGB values above. Our code for this Now let's create a classic rainbow animation that cycles smoothly across all 8 LEDs. This introduces the concept of **animation on LED strips**: update pixel colors, call `show()`, wait a bit, repeat. It's the same pattern we used for the [bouncing ball](oled.md#activity-draw-a-bouncing-ball) on the OLED. -{% highlight C++ %} +```cpp #include const int LED_PIN = 2; @@ -456,7 +456,7 @@ void loop() { delay(20); // ~50 fps } -{% endhighlight C++ %} +``` Try changing the `HUE_STEP` constant to `64` (slower rainbow) or `512` (faster rainbow). What happens if you change `setBrightness()` to 255? (Shield your eyes!). @@ -490,7 +490,7 @@ Use the same LED wiring as before, and add a 10KΩ potentiometer with its wiper #### The code -{% highlight C++ %} +```cpp #include const int LED_PIN = 2; @@ -527,7 +527,7 @@ void loop() { delay(20); } -{% endhighlight C++ %} +``` As you turn the potentiometer, you should see all 8 LEDs smoothly cycle through the rainbow together. @@ -549,7 +549,7 @@ Now let's add a **second potentiometer** on `A1` to independently control bright **Video.** A circuit diagram for the potentiometer-controlled hue and brightness example. You can view and play with this example on [Tinkercad](https://www.tinkercad.com/things/53EaKIvUCsX-neopixel-strip-8-pot-controlled-hue-and-brightness). {: .fs-1 } -{% highlight C++ %} +```cpp #include const int LED_PIN = 2; @@ -590,7 +590,7 @@ void loop() { delay(20); } -{% endhighlight C++ %} +``` Try turning each knob independently—you can dial in any color at any brightness level. Notice how the `ColorHSV()` function's three parameters (hue, saturation, value) map perfectly to physical controls. What would you use a *third* potentiometer for? (Hint: saturation controls how vivid *vs.* pastel the color looks!) @@ -608,7 +608,7 @@ For our final activity, let's build a **level meter** (or VU meter)—a bar-grap We'll color the LEDs from green (low) through yellow (mid) to red (high), like a classic audio level meter. -{% highlight C++ %} +```cpp #include const int LED_PIN = 2; @@ -653,7 +653,7 @@ void loop() { delay(30); } -{% endhighlight C++ %} +``` Turn the potentiometer and watch the LEDs fill up like a progress bar! This is a simple but satisfying example of mapping data to a physical display. Try replacing the potentiometer with a [force-sensitive resistor](../arduino/force-sensitive-resistors.md) or a [photoresistor](../sensors/photoresistors.md) for a more interactive experience. diff --git a/advancedio/oled.md b/advancedio/oled.md index d7b294bb..ee512f63 100644 --- a/advancedio/oled.md +++ b/advancedio/oled.md @@ -218,7 +218,7 @@ Below, we describe how to draw shapes, text, and bitmaps. Importantly, when you Let's begin by drawing a circle at `x,y` location of `50,20` with a radius of `10`. We'll start first with pseudocode to understand the drawing pipeline, then show actual C++. -``` +```text // One-time initialization Adafruit_SSD1306 _disp(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); _disp.begin(SSD1306_SWITCHCAPVCC, 0x3D); // Allocate RAM for image buffer, set VCC, and I2C address @@ -231,7 +231,7 @@ _disp.display(); // Render offscreen buffer to displ And here's the actual C++ implementation (the full code is on GitHub as [DrawCircle.ino](https://github.com/makeabilitylab/arduino/blob/master/OLED/DrawCircle/DrawCircle.ino)). -{% highlight C++ %} +```cpp // Instantiate SSD1306 driver display object Adafruit_SSD1306 _display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); @@ -259,11 +259,11 @@ void loop(){ // Render graphics buffer to screen _display.display(); } -{% endhighlight C++ %} +``` Now, because we are drawing the exact same thing on every `loop()` call, we could just as well put this drawing code into `setup()` and have it draw once and only once (the graphic content will persist). -{% highlight C++ %} +```cpp // Instantiate SSD1306 driver display object Adafruit_SSD1306 _display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); @@ -291,7 +291,7 @@ void loop(){ // Empty on purpose to make a point about how graphic content persists // on screen } -{% endhighlight C++ %} +``` However, for practical purposes, we always want to put our drawing methods in `loop()` because we want to support **dynamic graphics**, which are animated (*e.g.,* graphics that change over time) and/or responsive (*e.g.,* graphics that change in response to input). @@ -348,21 +348,21 @@ Rather than call `Serial.print("Hello World")`, however, with the OLED display a To use the OLED's print functionality, you can first set optional parameters such as the text color, size, and wrapping: -{% highlight C++ %} +```cpp void setTextColor(uint16_t color); void setTextColor(uint16_t color, uint16_t backgroundcolor); void setTextSize(uint8_t size); void setTextWrap(boolean w); -{% endhighlight C++ %} +``` Then, to position the text, you set the print cursor with: -{% highlight C++ %} +```cpp void setCursor(uint16_t x0, uint16_t y0); -{% endhighlight C++ %} +``` Finally, to print the text at that cursor position, you can call any of the standard [`Serial.print`](https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Print.h) methods, including this subset: -{% highlight C++ %} +```cpp size_t print(const String &); size_t print(const char[]); size_t print(char); @@ -370,7 +370,7 @@ size_t print(char); size_t println(const String &s); size_t println(const char[]); size_t println(char); -{% endhighlight C++ %} +``` See the [Serial.print() docs](https://www.arduino.cc/reference/en/language/functions/communication/serial/print/) or the [Print.h](https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/Print.h) library for more on the `print` API, or read on for an example. @@ -378,7 +378,7 @@ See the [Serial.print() docs](https://www.arduino.cc/reference/en/language/funct In creative coding, visualization, and game dev, we often want to center or otherwise align text. To do so, we need to **measure** it. Fortunately, the [Adafruit GFX](https://github.com/adafruit/Adafruit-GFX-Library/blob/master/Adafruit_GFX.h) library has a method called `getTextBounds` that does just that! -{% highlight C++ %} +```cpp /**************************************************************************/ /*! @brief Helper to determine size of a string with current font/size. @@ -393,11 +393,11 @@ In creative coding, visualization, and game dev, we often want to center or othe */ /**************************************************************************/ void getTextBounds(String &str, int16_t x, int16_t y, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h); -{% endhighlight C++ %} +``` For example, in our [HelloWorld.ino](https://github.com/makeabilitylab/arduino/blob/master/OLED/HelloWorld/HelloWorld.ino) example, we center the text "Hello Makers!" both vertically and horizontally on the OLED screen. The key excerpt is here: -{% highlight C++ %} +```cpp int16_t x, y; uint16_t textWidth, textHeight; const char strHello[] = "Hello Makers!"; @@ -418,7 +418,7 @@ _display.print(strHello); // Render the graphics buffer to screen _display.display(); -{% endhighlight C++ %} +``` ##### Inverting text @@ -440,7 +440,7 @@ While you can use either `drawChar` or `write`, the latter uses the currently se To draw a happy face—which is char index `2`—in the middle of the screen, for example, we could use `drawChar`: -{% highlight C++ %} +```cpp const int CHAR_WIDTH = 5; const int CHAR_HEIGHT = 8; @@ -453,11 +453,11 @@ uint16_t yText = _display.height() / 2 - charHeight / 2; uint16_t xText = _display.width() / 2 - charWidth / 2; _display.drawChar(xText, yText, (char)charIndex, SSD1306_WHITE, SSD1306_BLACK, charSize); -{% endhighlight C++ %} +``` Or we could also use the `write()` method: -{% highlight C++ %} +```cpp int16_t x1, y1; uint16_t textWidth, textHeight; int charIndex = 2; // for smiley face @@ -468,7 +468,7 @@ uint16_t yText = _display.height() / 2 - textHeight / 2; uint16_t xText = _display.width() / 2 - textWidth / 2; _display.setCursor(xText, yText); _display.write(charIndex); -{% endhighlight C++ %} +``` Here's an [example](https://github.com/makeabilitylab/arduino/blob/master/OLED/DrawChar/DrawChar.ino) iterating through all of the glyphs individually, which demonstrates the code above. You can use either `drawChar` or `write`—we demonstrate both in [DrawChar.ino](https://github.com/makeabilitylab/arduino/blob/master/OLED/DrawChar/DrawChar.ino). @@ -510,7 +510,7 @@ First, to get a feel for the Adafruit GFX API and the coordinate system, let's s Remember, in `loop()`, you need to: -{% highlight C++ %} +```cpp // Clear the display. If we don't do this, we'll simply be drawing over our // previous renderings (which you may sometimes want but generally not) _display.clearDisplay(); @@ -520,7 +520,7 @@ drawStuff(); // Render graphics buffer to screen _display.display(); -{% endhighlight C++ %} +``` We made a version called [SimpleDrawingDemo.ino](https://github.com/makeabilitylab/arduino/blob/master/OLED/SimpleDrawingDemo/SimpleDrawingDemo.ino) that draws shapes of random sizes and locations on **each frame**, but you could do something even simpler (or more complex)! @@ -574,7 +574,7 @@ For the C++ implementation using the Adafruit GFX library and Arduino, the key b Again, rather than, say, "miles per hour" or "pixels per second", we've defined speed as "pixels per frame"—that is, how many pixels does the object move per frame. If we set `_xSpeed` to 5 and `_ySpeed` to 0, then the ball would move 5 pixels in x per frame (and simply bounce back and forth from the left side of the screen to the right and back again). -{% highlight C++ %} +```cpp // Create the display object Adafruit_SSD1306 _display(128, 64, &Wire, 4); @@ -619,7 +619,7 @@ void loop() { // Render buffer to screen _display.display(); } -{% endhighlight C++ %} +``` You can view the full code on GitHub as [BallBounce.ino](https://github.com/makeabilitylab/arduino/blob/master/OLED/BallBounce/BallBounce.ino). @@ -659,7 +659,7 @@ Here's the circuit. Same as before but we've added a 10K potentiometer. The code is simple: read the analog input and use it to set the circle's radius. -{% highlight C++ %} +```cpp void loop() { // On each loop, we'll want to clear the display so we're not writing over // previously drawn data @@ -686,7 +686,7 @@ void loop() { delay(50); } -{% endhighlight C++ %} +``` You can view the full code on GitHub as [AnalogBallSize.ino](https://github.com/makeabilitylab/arduino/blob/master/OLED/AnalogBallSize/AnalogBallSize.ino). @@ -706,7 +706,7 @@ Now let's hook up **two** analog inputs to control the x,y location of the circl For the code, it's very similar to [AnalogBallSize.ino](https://github.com/makeabilitylab/arduino/blob/master/OLED/AnalogBallSize/AnalogBallSize.ino), but we translate the `analogRead` values to x and y locations: -{% highlight C++ %} +```cpp void loop() { // On each loop, we'll want to clear the display so we're not writing over // previously drawn data @@ -729,7 +729,7 @@ void loop() { delay(50); } -{% endhighlight C++ %} +``` You can view the full code on GitHub as [AnalogBallLocation.ino](https://github.com/makeabilitylab/arduino/blob/master/OLED/AnalogBallLocation/AnalogBallLocation.ino). @@ -749,7 +749,7 @@ The idea is simple: read in a sensor value as `sensorVal`, draw a vertical line Notably, this code takes advantage of **selectively** calling `_display.clearDisplay()`. Unlike the other examples we've shared thus far—which clear the display on each frame—here, we take advantage of graphics persisting across `_display.display()` calls to "build up" our graph over time. That is, we only draw **one** new line per new sensor input, which persists on the screen until `_xPos >= _display.width()`, at which point we call `_display.clearDisplay()`. -{% highlight C++ %} +```cpp void loop() { // Read the analog voltage value @@ -773,7 +773,7 @@ void loop() { delay(10); } -{% endhighlight C++ %} +``` The full source code is available in our [OLED GitHub](https://github.com/makeabilitylab/arduino/tree/master/OLED) as [AnalogGraph.ino](https://github.com/makeabilitylab/arduino/blob/master/OLED/AnalogGraph/AnalogGraph.ino). Here's a video demo: @@ -789,7 +789,7 @@ A slightly improved but more complicated version of the analog graph is a **scro Look over the code. Does it make sense? -{% highlight C++ %} +```cpp int _circularBuffer[SCREEN_WIDTH]; //fast way to store values int _curWriteIndex = 0; // tracks where we are in the circular buffer @@ -827,7 +827,7 @@ void loop() { delay(10); } -{% endhighlight C++ %} +``` The full source code is available in our [OLED GitHub](https://github.com/makeabilitylab/arduino/tree/master/OLED) as [AnalogGraphScrolling.ino](https://github.com/makeabilitylab/arduino/blob/master/OLED/AnalogGraphScrolling/AnalogGraphScrolling.ino). Here's a video demo. diff --git a/advancedio/servo.md b/advancedio/servo.md index a9f6af68..23de0551 100644 --- a/advancedio/servo.md +++ b/advancedio/servo.md @@ -137,7 +137,7 @@ The [Arduino Servo library](https://github.com/arduino-libraries/Servo/blob/mast ### Key API -{% highlight C++ %} +```cpp #include Servo myServo; // Create a Servo object @@ -146,7 +146,7 @@ void setup() { myServo.attach(3); // Attach to pin 3 (any digital pin works) myServo.write(90); // Move to 90° (center position) } -{% endhighlight C++ %} +``` Here are the most commonly used functions: @@ -188,7 +188,7 @@ If you visit the [GitHub source tree for the Servo library](https://github.com/a If you look at [Servo.h](https://github.com/arduino-libraries/Servo/blob/master/src/Servo.h), you'll see how the library selects the right implementation: -{% highlight C++ %} +```cpp #if defined(ARDUINO_ARCH_AVR) #include "avr/ServoTimers.h" #elif defined(ARDUINO_ARCH_SAM) @@ -200,7 +200,7 @@ If you look at [Servo.h](https://github.com/arduino-libraries/Servo/blob/master/ #else #error "This library only supports boards with an AVR, SAM, SAMD, NRF52..." #endif -{% endhighlight C++ %} +``` Servo libraries rely heavily on hardware timers. Since an Arduino Uno (AVR) and a Nano 33 IoT (SAMD) have completely different timer hardware, the library must maintain separate codebases for each. If you look inside the `avr` folder, you'll find custom [Servo.cpp](https://github.com/arduino-libraries/Servo/blob/master/src/avr/Servo.cpp) and [ServoTimers.h](https://github.com/arduino-libraries/Servo/blob/master/src/avr/ServoTimers.h) code specifically written to manipulate ATmega registers—this is why the Servo library "claims" Timer1 and disables `analogWrite()` on certain pins, as noted above. @@ -264,7 +264,7 @@ Now that we understand how servos work and have one wired up, let's build some p Just as we started with [blinking an LED](../arduino/led-blink.md) and [lighting up NeoPixels](addressable-leds.md#activity-1-light-em-up), let's start with the simplest possible servo program: sweeping back and forth between 0° and 180°. This confirms your wiring is correct and that the library is communicating with the servo. -{% highlight C++ %} +```cpp #include const int SERVO_PIN = 3; @@ -287,7 +287,7 @@ void loop() { delay(15); } } -{% endhighlight C++ %} +``` @@ -313,7 +313,7 @@ Use the same servo wiring as before, and add a 10KΩ potentiometer with its wipe #### The code -{% highlight C++ %} +```cpp #include const int SERVO_PIN = 3; @@ -344,7 +344,7 @@ void loop() { delay(15); } -{% endhighlight C++ %} +``` @@ -384,7 +384,7 @@ Use the same servo + potentiometer wiring, and add two tactile buttons on Pins 8 #### The code -{% highlight C++ %} +```cpp #include const int SERVO_PIN = 3; @@ -452,7 +452,7 @@ void loop() { delay(15); } -{% endhighlight C++ %} +``` diff --git a/advancedio/smoothing-input.md b/advancedio/smoothing-input.md index 7cb37633..1b3a52ee 100644 --- a/advancedio/smoothing-input.md +++ b/advancedio/smoothing-input.md @@ -119,7 +119,7 @@ Below, we compute three different moving average filter window sizes: 5, 10, and The official Arduino [signal smoothing tutorial](https://www.arduino.cc/en/Tutorial/BuiltInExamples/Smoothing) uses a moving average filter. Cleverly, their code uses an optimization (which we borrow below) to avoid iterating over the entire window to compute each new average. Instead, we simply subtract the least recent reading in our sliding window from a running total. The code also uses a [circular buffer](https://en.wikipedia.org/wiki/Circular_buffer) to eliminate needless memory allocations, which is important on constrained systems like microcontrollers. -{% highlight C %} +```cpp // read the sensor value int sensorVal = analogRead(SENSOR_INPUT_PIN); @@ -144,7 +144,7 @@ if (_curReadIndex >= SMOOTHING_WINDOW_SIZE) { // ...wrap around to the beginning: _curReadIndex = 0; } -{% endhighlight C %} +``` #### Arduino code @@ -227,7 +227,7 @@ The coefficient $$\alpha$$ determines the exponential dropoff. A higher $$\alpha If you're not used to reading equations, then perhaps the Arduino code below is more clear. The algorithm is quite straightforward and, again, unlike the traditional moving average algorithm, does not require a window buffer! -{% highlight C %} +```cpp const int SENSOR_INPUT_PIN = A0; float _ewmaAlpha = 0.1; // the EWMA alpha value (α) @@ -251,7 +251,7 @@ void loop() Serial.println(_ewma); delay(50); // Reading new values at ~20Hz } -{% endhighlight C %} +``` **Code.** [This code](https://github.com/makeabilitylab/arduino/blob/master/Filters/ExponentialWeightedMovingAverageFilter/ExponentialWeightedMovingAverageFilter.ino) is on GitHub. {: .fs-1 } diff --git a/advancedio/vibromotor.md b/advancedio/vibromotor.md index 49e81192..4e0bab2c 100644 --- a/advancedio/vibromotor.md +++ b/advancedio/vibromotor.md @@ -378,7 +378,7 @@ Now that we understand the theory and have our circuit wired up, let's build thr Just as we started our Arduino journey by [blinking an LED](../arduino/led-blink.md), let's start here by "blinking" a vibration motor—turning it on and off at a regular interval. This is the simplest possible vibromotor program and confirms that your circuit is working. -{% highlight C++ %} +```cpp const int VIBRO_PIN = 3; // Connect to the transistor base (via 1K resistor) void setup() { @@ -391,7 +391,7 @@ void loop() { digitalWrite(VIBRO_PIN, LOW); // Motor off delay(500); // Pause for 500ms } -{% endhighlight C++ %} +``` Try experimenting with different on/off durations. What happens with very short pulses (*e.g.,* 50ms on, 200ms off)? Can you feel the difference between a 100ms buzz and a 500ms buzz? This is the foundation of haptic pattern design! @@ -407,7 +407,7 @@ Use the same transistor circuit as before, and add a 10KΩ potentiometer with it #### The code -{% highlight C++ %} +```cpp const int VIBRO_PIN = 3; // PWM pin connected to transistor base const int POT_PIN = A0; // Potentiometer wiper const int MAX_ANALOG_INPUT = 1023; // 10-bit ADC @@ -435,7 +435,7 @@ void loop() { delay(50); } -{% endhighlight C++ %} +``` As you turn the potentiometer, you should feel the vibration intensity change smoothly from off (fully counter-clockwise) to maximum (fully clockwise). Open the Serial Monitor to see the mapping from analog input to PWM output. Can you feel differences at low PWM values (*e.g.,* between 20 and 50)? At what PWM value does the motor stop vibrating entirely? @@ -453,7 +453,7 @@ Use the same transistor + vibromotor circuit, and add three tactile buttons conn #### The code -{% highlight C++ %} +```cpp const int VIBRO_PIN = 3; const int BTN_TAP = 8; const int BTN_ALERT = 9; @@ -506,7 +506,7 @@ void playAlarm() { } delay(300); } -{% endhighlight C++ %} +``` Try pressing each button and feeling the difference. Can you tell the three patterns apart with your eyes closed? Try designing your own patterns—a heartbeat (*thump-thump... thump-thump...*), a countdown, or a rhythmic pattern. Haptic design is all about creating distinct, recognizable tactile signatures. diff --git a/arduino/buttons.md b/arduino/buttons.md index 80615aff..6f0f93de 100644 --- a/arduino/buttons.md +++ b/arduino/buttons.md @@ -183,7 +183,7 @@ However, if you do this, what will the digital input pin read when the switch is In fact, try wiring up this configuration yourself and running the following program with the [Serial Monitor](serial-print.md#step-3-open-serial-monitor-in-the-arduino-ide) open. What happens when you press the button? Try touching the button legs with your fingers but not actually pressing the button—what happens to the `digitalRead` value? Are you reliably tracking the button state? -{% highlight cpp %} +```cpp const int INPUT_BUTTON_PIN = 2; void setup() { @@ -197,7 +197,7 @@ void loop() Serial.println(buttonVal); // print value to Serial delay(5); // small delay } -{% endhighlight cpp %} +``` Here's a quick video demonstration of what happens—the floating pin problem! Note: we are using a slightly modified version of this code where an LED is turned on if the button is pressed (*i.e.,* if `buttonVal == 1`). This just makes it easier to see the fluctuating button state. @@ -374,7 +374,7 @@ To zoom in on this image, right-click and select 'Open image in a new tab.' ### Code to turn on LED with button press -{% highlight cpp %} +```cpp const int INPUT_BUTTON_PIN = 2; const int OUTPUT_LED_PIN = LED_BUILTIN; @@ -391,7 +391,7 @@ void loop() digitalWrite(OUTPUT_LED_PIN, buttonState); delay(30); } -{% endhighlight cpp %} +``` ### Tinkercad version with no breadboard diff --git a/arduino/debouncing.md b/arduino/debouncing.md index 1dbea63f..280fd433 100644 --- a/arduino/debouncing.md +++ b/arduino/debouncing.md @@ -150,7 +150,7 @@ For our first and most basic solution, we will read the button state, wait a giv We're going to use [`delay`](https://www.arduino.cc/reference/en/language/functions/time/delay/) here to wait for the "debouncing window" time period, which we already know should generally be avoided but is sometimes helpful and appropriate (if it's not negatively impacting the responsiveness of your program, for example). -{% highlight cpp %} +```cpp const int BUTTON_INPUT_PIN = 2; const int LED_OUTPUT_PIN = 3; @@ -186,7 +186,7 @@ void loop() { // Write out HIGH or LOW digitalWrite(LED_OUTPUT_PIN, _savedButtonVal); } -{% endhighlight cpp %} +``` This [source code](https://github.com/makeabilitylab/arduino/blob/master/Basics/digitalRead/DebounceWithDelays/DebounceWithDelays.ino) is on GitHub. {: .fs-1 } @@ -235,7 +235,7 @@ This solution is nicely captured by user [cdvma](https://www.reddit.com/r/embedd Here's a quick implementation: -{% highlight cpp %} +```cpp const int BUTTON_INPUT_PIN = 2; const int LED_OUTPUT_PIN = 3; @@ -267,7 +267,7 @@ void loop() { // Write out HIGH or LOW digitalWrite(LED_OUTPUT_PIN, _savedButtonVal); } -{% endhighlight cpp %} +``` This solution is less robust but works well for human input in environments with limited electrical noise (see this [Reddit discussion](https://www.reddit.com/r/embedded/comments/gf74p8/reliable_user_input_with_unreliable_physical/fprrygg?utm_source=share&utm_medium=web2x&context=3)). However, as is pointed out in the Reddit thread ([link](https://www.reddit.com/r/embedded/comments/gf74p8/reliable_user_input_with_unreliable_physical/fpw7xpf?utm_source=share&utm_medium=web2x&context=3)), this simple solution does not protect against electrostatic discharge (ESD) and thus fails regulatory requirements (which require the two state reads like we did in Solutions 1 and 2). diff --git a/arduino/fast-analog-read.md b/arduino/fast-analog-read.md index f1cbbe6d..ba93e315 100644 --- a/arduino/fast-analog-read.md +++ b/arduino/fast-analog-read.md @@ -38,7 +38,7 @@ This [blog post ](http://yaab-arduino.blogspot.com/2015/02/fast-sampling-from-an Inspired by the discussion on this [Sparkfun blog post](https://learn.sparkfun.com/blog/1687#comments), I looked up the source code directly to investigate. The entire `int main(void)` function in [main.cpp](https://github.com/arduino/ArduinoCore-avr/blob/2f67c916f6ab6193c404eebe22efe901e0f9542d/cores/arduino/main.cpp) is: -```C +```cpp int main(void) { init(); @@ -64,7 +64,7 @@ From this [Arduino forum post](https://forum.arduino.cc/index.php?topic=4324.msg You can also manipulate the ports directly rather than via the Arduino libraries (which incur lots of overhead). From the same forum post: -```C +```cpp cli(); while (1) { PORTD |= B1000; diff --git a/arduino/force-sensitive-resistors.md b/arduino/force-sensitive-resistors.md index 3052cac4..d8d2c296 100644 --- a/arduino/force-sensitive-resistors.md +++ b/arduino/force-sensitive-resistors.md @@ -203,11 +203,11 @@ If, instead, we can't assume that both ranges start at zero, the more general co This type of range conversion is so common that Arduino (and Processing and many other programming libraries) have a built-in function for it called [`map`](https://www.arduino.cc/reference/en/language/functions/math/map/). It's called "map" because we want to re-map a number from one range to another. Indeed, here's the entire `map` function from the Arduino source code—notice it also multiplies before dividing: -{% highlight cpp %} +```cpp long map(long x, long in_min, long in_max, long out_min, long out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } -{% endhighlight cpp %} +``` Importantly, as the [docs](https://www.arduino.cc/reference/en/language/functions/math/map/) make abundantly clear, notice that this built-in method uses **integer** math and so will not return fractions (floats). If you need more precise conversions, implement your own mapping function (which also provides the opportunity to implement non-linear conversions like logarithmic mappings). diff --git a/arduino/inside-arduino.md b/arduino/inside-arduino.md index ff0498c3..466c23ed 100644 --- a/arduino/inside-arduino.md +++ b/arduino/inside-arduino.md @@ -32,7 +32,7 @@ Here are some common answers. Note: I have not stress tested them all and I'm su **First**, perhaps the simplest way is to cast everything as a String and use string concatenation: -``` C +```cpp Serial.println((String)"Var 1:" + var1 + " Var 2:" + var2 + " Var 3:" + var3); ``` [Source](https://arduino.stackexchange.com/a/69566) @@ -42,11 +42,11 @@ Note: you should only do this for rapid prototypes because of memory inefficienc **Second**, use `snprintf` to format into a character buffer. This is the standard C/C++ approach and avoids the memory fragmentation problems of the `String` class: -{% highlight cpp %} +```cpp char buffer[64]; // make sure this is large enough for your formatted string snprintf(buffer, sizeof(buffer), "Var 1: %d Var 2: %d Var 3: %d", var1, var2, var3); Serial.println(buffer); -{% endhighlight cpp %} +``` {: .note } > On standard Arduino AVR boards, `snprintf` does **not** support floating-point format specifiers (`%f`) out of the box—this is disabled to save memory. To format floats, use [`dtostrf()`](https://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#ga060c998e77fb5fc0d3168b3ce8571571) to convert the float to a string first, then include it in your `snprintf` call. @@ -55,7 +55,7 @@ Serial.println(buffer); **Fourth**, you could redirect `printf` to Serial output: -{% highlight cpp %} +```cpp // Function that printf and related will use to print int serial_putchar(char c, FILE* f) { if (c == '\n') serial_putchar('\r', f); @@ -81,7 +81,7 @@ void loop() { delay(1); } } -{% endhighlight cpp %} +``` [Source](https://arduino.stackexchange.com/a/480) and [discussion](https://forum.arduino.cc/index.php/topic,120440.0.html) ## What's calling loop() and how fast? @@ -90,7 +90,7 @@ Because Arduino is [open source](https://github.com/arduino), we can look up the In short, `loop()` is called within an infinite `for` (or `while` loop). The only overhead is checking for whether there is data available on the serial port and then reading the serial buffers. The entire `int main(void)` function in [main.cpp](https://github.com/arduino/ArduinoCore-avr/blob/2f67c916f6ab6193c404eebe22efe901e0f9542d/cores/arduino/main.cpp) is: -{% highlight cpp %} +```cpp int main(void) { init(); @@ -108,11 +108,11 @@ int main(void) } return 0; } -{% endhighlight cpp %} +``` Interestingly, this [Arduino forum post](https://forum.arduino.cc/index.php?topic=615714.0) suggests that because `serialEventRun()` is weakly defined in the core, you can define it locally in your sketch to override the default definition, which, according to the OP, will "save a little memory and makes the loop() run a little faster too!" You can do this if you don't need to use serial communication. -{% highlight cpp %} +```cpp void serialEventRun() {} void setup() { @@ -120,7 +120,7 @@ void setup() { void loop() { } -{% endhighlight cpp %} +``` ## Converting analogRead to voltages @@ -161,7 +161,7 @@ As you might expect—given our warnings about avoiding overuse of [`delay(unsig The [`delay(unsigned long ms)`](https://www.arduino.cc/reference/en/language/functions/time/delay/) function is found in [wiring.c](https://github.com/arduino/ArduinoCore-avr/blob/2f67c916f6ab6193c404eebe22efe901e0f9542d/cores/arduino/wiring.c) and is, in its entirety, copied below: -{% highlight cpp %} +```cpp void delay(unsigned long ms) { uint32_t start = micros(); @@ -174,7 +174,7 @@ void delay(unsigned long ms) } } } -{% endhighlight cpp %} +``` ## How does `digitalWrite()` work internally? @@ -228,7 +228,7 @@ If you bypass the Arduino framework entirely, you can write code directly for th Here is what a standard "Blinky" sketch looks like in bare-metal C for the **Arduino Uno** (ATmega328P). On the Uno, the built-in LED (Pin 13) is hardwired to bit 5 of Port B (`PB5`). According to Section 13.2.1 of the ATmega328P datasheet, the `DDxn` bit in the Data Direction Register (`DDRx`) selects the pin's direction, and the `PORTxn` bit in the Data Register sets the output logic level. -{% highlight cpp %} +```cpp #ifndef F_CPU #define F_CPU 16000000UL // 16 MHz clock speed #endif @@ -252,11 +252,11 @@ int main(void) { // never reached return 0; } -{% endhighlight cpp %} +``` If we want to run this exact same bare-metal Blinky on an **Arduino Leonardo** (ATmega32U4), the code has to change. Because of the Leonardo's internal USB hardware, the built-in LED (Pin 13) is wired to bit 7 of Port C (`PC7`). -{% highlight cpp %} +```cpp #ifndef F_CPU #define F_CPU 16000000UL #endif @@ -278,7 +278,7 @@ int main(void) { // never reached return 0; } -{% endhighlight cpp %} +``` ### The Takeaway: Hardware Abstraction diff --git a/arduino/led-blink.md b/arduino/led-blink.md index b20189d1..6840c7ba 100644 --- a/arduino/led-blink.md +++ b/arduino/led-blink.md @@ -159,24 +159,24 @@ Start a new sketch in the Arduino IDE: Because the 20 digital I/O pins can be used for **either** **input** or **output**, we need to specify that Pin 3 should be used for *output*. That is, we want the Arduino to **output** a 5V signal on Pin 3 to turn on our LED. We configure pins in the `setup()` block and use the [`pinMode(int pin, int mode)`](https://www.arduino.cc/reference/en/language/functions/digital-io/pinmode/) command, which takes in a pin as the first parameter and a mode (`INPUT` or `OUTPUT`) as the second. -{% highlight C %} +```cpp void setup() { // put your setup code here, to run once: pinMode(3, OUTPUT); } -{% endhighlight C %} +``` ### Step 3: Set Pin 3 HIGH Lastly, we need to actually set the Pin 3 signal to `HIGH`. For this, we use the [`digitalWrite(int pin, int value)`](https://www.arduino.cc/reference/en/language/functions/digital-io/digitalwrite/) command, which takes in a pin as the first parameter and a value (`HIGH` or `LOW`) as the second. We could do this either in `setup()` or in `loop()` but since we're not currently changing the output signal, there is no reason to put it in `loop()`, so let's put it in `setup()` along with the `pinMode` code. -{% highlight C %} +```cpp void setup() { // put your setup code here, to run once: pinMode(3, OUTPUT); digitalWrite(3, HIGH); // turn LED on (output 5V) } -{% endhighlight C %} +``` ### Step 4: Compile the code @@ -212,7 +212,7 @@ Now, let's modify our code to turn on *and* off the LED programmatically. More s First, move the digitalWrite code from `setup()` to `loop()`: -{% highlight C %} +```cpp void setup() { // set Pin 3 to output pinMode(3, OUTPUT); @@ -221,13 +221,13 @@ void setup() { void loop() { digitalWrite(3, HIGH); // turn LED on (output 5V) } -{% endhighlight C %} +``` ### Step 2: Add in delays and code to turn off LED Now, add in code to pause (for one second) and then turn off the LED (for one second) using `delay()`. Remember, when `loop()` completes, it is automatically called again (making the LED blink continuously). -{% highlight C %} +```cpp void setup() { // set Pin 3 to output pinMode(3, OUTPUT); @@ -239,7 +239,7 @@ void loop() { digitalWrite(3, LOW); // turn LED off (output 0V) delay(1000); // wait another second } -{% endhighlight C %} +``` ### Step 3: Compile and upload @@ -253,7 +253,7 @@ We're done! Now, compile and upload the code and see it run! Typically, we want to limit the use of *literal constants* in our code and replace them by variables. In this case, let's replace `3` with `LED_OUTPUT_PIN` defined as a global variable at the top of our program (`const int LED_OUTPUT_PIN = 3;`). This will make our code more maintainable, more readable, and less prone to accidental mistakes. Try to do this for all literals in the future. -{% highlight C %} +```cpp const int LED_OUTPUT_PIN = 3; void setup() { // set Pin 3 to output @@ -266,7 +266,7 @@ void loop() { digitalWrite(LED_OUTPUT_PIN, LOW); // turn LED off (output 0V) delay(1000); // wait another second } -{% endhighlight C %} +``` ### Walking through the code @@ -347,7 +347,7 @@ Because `delay()` usage can be so troublesome, as part of their introductory tut To avoid `delay()` calls, the code tracks **time**, **LED state changes** (when the LED switches from `HIGH` to `LOW` or `LOW` to `HIGH`), and **when** these state changes occur. The [BlinkWithoutDelay](https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay) main loop is below. Notice that there are no `delay()` calls! -{% highlight C %} +```cpp void loop() { // check to see if it's time to blink the LED; that is, if the difference // between the current time and last time you blinked the LED is bigger than @@ -369,7 +369,7 @@ void loop() { digitalWrite(ledPin, ledState); } } -{% endhighlight C %} +``` We've also made our own [BlinkWithoutDelay](https://github.com/makeabilitylab/arduino/blob/master/Basics/digitalWrite/BlinkWithoutDelay/BlinkWithoutDelay.ino) version, which is available on [GitHub](https://github.com/makeabilitylab/arduino/blob/master/Basics/digitalWrite/BlinkWithoutDelay/BlinkWithoutDelay.ino) and shown below. This version is functionally equivalent to Arduino's official example but uses our own coding style and is, in our opinion, more understandable. diff --git a/arduino/led-blink2.md b/arduino/led-blink2.md index 0f5ad774..8c40ee74 100644 --- a/arduino/led-blink2.md +++ b/arduino/led-blink2.md @@ -83,7 +83,7 @@ Let's write the code! ### Step 1: Write the setup and initialization code -{% highlight C %} +```cpp const int LED1_OUTPUT_PIN = 3; // Anode faces Pin 3 (cathode connected to 0V) const int LED2_OUTPUT_PIN = 4; // Cathode faces Pin 4 (anode connected to 5V) const int DELAY_MS = 1000; // delay for 1 sec between blinks @@ -94,11 +94,11 @@ void setup() { pinMode(LED1_OUTPUT_PIN, OUTPUT); pinMode(LED2_OUTPUT_PIN, OUTPUT); } -{% endhighlight C %} +``` ### Step 2: Write the blink code in loop() -{% highlight C %} +```cpp // The loop function runs over and over again forever void loop() { // Below, you're going to see that driving Pin 3 HIGH will turn on LED1 @@ -111,7 +111,7 @@ void loop() { digitalWrite(LED2_OUTPUT_PIN, LOW); // turns ON LED2 (Pin 4 is now 0V and other leg of LED is 5V) delay(DELAY_MS); // wait for a second } -{% endhighlight C %} +``` ### Step 3: Compile, upload, and run the code! diff --git a/arduino/led-blink3.md b/arduino/led-blink3.md index 3dacb5f8..16cd324c 100644 --- a/arduino/led-blink3.md +++ b/arduino/led-blink3.md @@ -31,7 +31,7 @@ As with our previous lesson on [crossfading RGB LEDs](rgb-led-fade.md), this les The canonical and beloved **first Arduino sketch**, [Blink](https://www.arduino.cc/en/tutorial/blink), enables beginners to quickly build and write code for a circuit. The code looks something like this, which we covered in our own [Blink lesson](led-blink.md): -{% highlight C %} +```cpp void setup() { // set Pin 3 to output pinMode(3, OUTPUT); @@ -43,7 +43,7 @@ void loop() { digitalWrite(3, LOW); // turn LED off (output 0V) delay(1000); // wait another second } -{% endhighlight C %} +``` Blink is easy. It's gratifying. But... it sets up a flawed mental model about how to structure programs and when/how to use [`delay()`](https://www.arduino.cc/reference/en/language/functions/time/delay/). @@ -94,7 +94,7 @@ For our initial approach, we need four things for each LED: For the **blink interval**, we'll use `const` variables like `LED1_BLINK_INTERVAL_MS`, `LED2_BLINK_INTERVAL_MS`, and `LED3_BLINK_INTERVAL_MS` -{% highlight C %} +```cpp const int LED1_OUTPUT_PIN = 2; const int LED1_BLINK_INTERVAL_MS = 200; // interval at which to blink LED1 (in milliseconds) @@ -103,13 +103,13 @@ const int LED2_BLINK_INTERVAL_MS = 333; // interval at which to blink LED2 (in m const int LED3_OUTPUT_PIN = 9; const int LED3_BLINK_INTERVAL_MS = 1111; // interval at which to blink LED3 (in milliseconds) -{% endhighlight C %} +``` #### Toggle timestamps and LED states For the **toggle timestamps** and **LED states**, we'll use variables like `_led1LastToggledTimestampMs` and `_led1State`. We can toggle `ledState` simply by: `ledState = !ledState`. -{% highlight C %} +```cpp unsigned long _led1LastToggledTimestampMs = 0; // tracks the last time LED1 was updated int _led1State = LOW; // will toggle between LOW and HIGH @@ -118,7 +118,7 @@ int _led2State = LOW; // will toggle between LOW and HIGH unsigned long _led3LastToggledTimestampMs = 0; // tracks the last time LED3 was updated int _led3State = LOW; // will toggle between LOW and HIGH -{% endhighlight C %} +``` To capture timestamps, we'll use Arduino's [`millis()` ](https://www.arduino.cc/reference/en/language/functions/time/millis/) function, which returns "*the number of **milliseconds** passed since the Arduino board began running the current program*" as an `unsigned long`. @@ -128,7 +128,7 @@ On the Arduino, the `unsigned long` data type is 32 bits (4 bytes), which ranges We then use the same general logic as the "blinking without delays" [covered previously](led-blink.md#blink-without-using-delay) for each LED: -{% highlight C %} +```cpp unsigned long currentTimestampMs = millis(); // Check to see if we reached the toggle state interval for LED1 @@ -146,7 +146,7 @@ if (currentTimestampMs - _led2LastToggledTimestampMs >= LED2_BLINK_INTERVAL_MS) } ... // and so on, copy the above block of code for each LED you're trying to blink -{% endhighlight C %} +``` #### Tracking timestamps and overflow @@ -158,7 +158,7 @@ Great question! And yes, it will still work! And the reason is because we are us For example, imagine that `_lastToggledTimestampMs` is `4,294,967,290` or `0xFFFFFFFA` in hexadecimal (32 bits), which is 5 milliseconds from overflow. And then imagine that `millis()` overflows (returns back to 0) and `currentTimestampMs` becomes, say, `1` or `0x00000001`. So, our subtraction is then: `0x00000001 - 0xFFFFFFFA`. There is `7` milliseconds difference between these two numbers, so we'd like the subtraction to result in `7`: -``` +```text 1. 0xFFFFFFFA 2. 0xFFFFFFFB 3. 0xFFFFFFFC @@ -171,7 +171,7 @@ For example, imagine that `_lastToggledTimestampMs` is `4,294,967,290` or `0xFFF And this is what we get! Feel free to experiment with this yourself by running the code below on you Arduino. We also suggest [this article](https://www.baldengineer.com/arduino-millis-plus-addition-does-not-add-up.html) by James Lewis about `millis()`, overflow, and arithmetic for more background. -{% highlight C %} +```cpp unsigned long _lastToggledTimestampMs = 4294967290; // change this to experiment with overflow void setup() { @@ -194,7 +194,7 @@ void loop() { _lastToggledTimestampMs++; } -{% endhighlight C %} +``` #### The full code for multi-rate blinking @@ -222,7 +222,7 @@ We're going to define a new class, called `Blinker`, which will greatly simplify Once we've made the `Blinker` class, our main code reduces to: -{% highlight C++ %} +```cpp Blinker _led1Blinker(2, 200); // specify pin and blink interval (200ms) Blinker _led2Blinker(5, 333); // specify pin and blink interval (333ms) Blinker _led3Blinker(9, 1111); // specify pin and blink interval (1111ms) @@ -238,7 +238,7 @@ void loop() { _led2Blinker.update(); _led3Blinker.update(); } -{% endhighlight C++ %} +``` But we have to make the `Blinker` class first, which we do below! @@ -254,7 +254,7 @@ To build our Blinker class, recall that we need four things per LED: For the `Blinker` class, we are simply going to convert these four things into member variables: -{% highlight C++ %} +```cpp class Blinker{ private: @@ -265,11 +265,11 @@ class Blinker{ unsigned long _lastToggledTimestamp; // last state toggle in ms ... // more here -{% endhighlight C++ %} +``` Finally, we need two functions: a `constructor` and `update()`—the latter which handles our core logic and toggling code and is intended to be called once per `loop()` iteration. We are going to declare these in the class definition itself: -{% highlight C++ %} +```cpp public: // Constructor Blinker(int pin, unsigned long blinkInterval) : @@ -293,7 +293,7 @@ Finally, we need two functions: a `constructor` and `update()`—the latter whic digitalWrite(_pin, _state); } } -{% endhighlight C++ %} +``` In order to use the `Blinker` class (as shown above), it needs to be defined within your `.ino` sketch at the top of the file (before you try to instantiate a Blinker object). Later, we'll also show how to create a class that exists in its own `.h` and `.cpp` files. diff --git a/arduino/led-fade.md b/arduino/led-fade.md index e2781756..bc8b7cea 100644 --- a/arduino/led-fade.md +++ b/arduino/led-fade.md @@ -129,7 +129,7 @@ Start a new sketch in the Arduino IDE: Our initialization code is the same as for [LED blink](led-blink.md) except for the addition of `const int MAX_ANALOG_OUT = 255;` and a constant for the delay amount of 5 milliseconds (`const int DELAY_MS = 5;`). -{% highlight C %} +```cpp const int LED_OUTPUT_PIN = 3; const int MAX_ANALOG_OUT = 255; // the max analog output on the Uno is 255 const int DELAY_MS = 5; @@ -138,13 +138,13 @@ void setup() { // set Pin 3 to output pinMode(LED_OUTPUT_PIN, OUTPUT); } -{% endhighlight C %} +``` ### Step 3: Write fade loop Now, write code that outputs steadily increasing values for [`analogWrite`](https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/) (to fade on) followed by steadily decreasing values (to fade off). -{% highlight C %} +```cpp void loop(){ // fade on for(int i = 0; i <= MAX_ANALOG_OUT; i += 1){ @@ -158,7 +158,7 @@ void loop(){ delay(DELAY_MS); } } -{% endhighlight C %} +``` The full code is embedded below: @@ -234,7 +234,7 @@ So, let's rewrite the fade example but without for loops and, instead, rely on t {: .note } > I have a habit of prefixing my global variables by `_` but this is just my own convention and helps me easily discern between local variables and global variables. You need not do this, of course! 😊 -{% highlight C %} +```cpp const int LED_OUTPUT_PIN = 3; const int MAX_ANALOG_OUT = 255; // the max analog output on the Uno is 255 const int DELAY_MS = 5; @@ -266,7 +266,7 @@ void loop() { // wait for some milliseconds to see the dimming effect delay(DELAY_MS); } -{% endhighlight C %} +``` You can find [this code in GitHub](https://github.com/makeabilitylab/arduino/blob/master/Basics/analogWrite/FadeOnAndOff/FadeOnAndOff.ino). diff --git a/arduino/piano.md b/arduino/piano.md index 39f37cc8..66661a90 100644 --- a/arduino/piano.md +++ b/arduino/piano.md @@ -90,7 +90,7 @@ The code is fairly straightforward. First, let's declare the waveform frequencies of our notes. -{% highlight cpp %} +```cpp // Frequencies (in Hz) of our piano keys // From: https://en.wikipedia.org/wiki/Piano_key_frequencies #define KEY_C 262 // 261.6256 Hz (middle C) @@ -98,7 +98,7 @@ First, let's declare the waveform frequencies of our notes. #define KEY_E 330 // 329.6276 Hz #define KEY_F 349 // 349.2282 Hz #define KEY_G 392 // 391.9954 Hz -{% endhighlight cpp %} +``` #### Step 2: Declare our pin constants @@ -106,7 +106,7 @@ Our piano has **five** buttons, so we need five input pins. We also need an outp Finally, we'll add in a boolean constant `_buttonsAreActiveLow` that simply lets us handle pull-up *vs.* pull-down logic in software. The boolean defaults to `true` because we assume a pull-up resistor configuration. Switch this to `false` if you decide to design your button circuits with pull-down resistors. -{% highlight cpp %} +```cpp // I lay out my buttons like piano keys. So, lower frequencies on left // and increasingly higher frequencies to the right // Change this depending on how you've laid out your keys @@ -123,13 +123,13 @@ const int OUTPUT_LED_PIN = LED_BUILTIN; // visual feedback on button press // Switch the following to false and change INPUT_PULLUP belows // to INPUT const boolean _buttonsAreActiveLow = true; -{% endhighlight cpp %} +``` #### Step 3: Setup our I/O pins in setup() In setup(), we simply initialize our **five inputs** and **two outputs** as inputs and outputs accordingly. Note the `INPUT_PULLUP` flag for each button input. -{% highlight cpp %} +```cpp void setup() { pinMode(INPUT_BUTTON_C_PIN, INPUT_PULLUP); pinMode(INPUT_BUTTON_D_PIN, INPUT_PULLUP); @@ -139,7 +139,7 @@ void setup() { pinMode(OUTPUT_PIEZO_PIN, OUTPUT); pinMode(OUTPUT_LED_PIN, OUTPUT); } -{% endhighlight cpp %} +``` #### Step 4: Write core piano logic in loop() @@ -147,7 +147,7 @@ Because tone() can only play one frequency at a time (darn, no rockin' chords), To more easily handle both pull-up and pull-down circuit configurations, we wrote a convenience function called `isButtonPressed(int btnPin)`, which abstracts the "isPressed" logic. (Though one might criticize the use of a global variable—tis common for these rapid prototypes, I'm afraid. You could pass the global var as a parameter into `isButtonPressed` if this makes you feel better about code modularity). -{% highlight cpp %} +```cpp void loop() { // tone() generates a square wave of the specified frequency (and 50% duty cycle) on a pin. @@ -189,7 +189,7 @@ boolean isButtonPressed(int btnPin){ // button is not pressed return false; } -{% endhighlight cpp %} +``` #### Step 5: Compile, upload, and run the code diff --git a/arduino/potentiometers-old.md b/arduino/potentiometers-old.md index 182daa76..b30fc9c4 100644 --- a/arduino/potentiometers-old.md +++ b/arduino/potentiometers-old.md @@ -340,7 +340,7 @@ Just like with our [button](buttons.md) lesson, let's walk through how one might Let's first introduce a simple program to read and print analog input values to Serial. This will provide a convenient way to test our input circuits. -{% highlight C %} +```cpp void setup() { Serial.begin(9600); // for printing values to console @@ -352,7 +352,7 @@ void loop() Serial.println(potVal); // print value to Serial delay(50); // Reading new values at ~20Hz } -{% endhighlight C %} +``` ### Building an initial circuit diff --git a/arduino/potentiometers.md b/arduino/potentiometers.md index dbc2f516..b06e078f 100644 --- a/arduino/potentiometers.md +++ b/arduino/potentiometers.md @@ -250,9 +250,9 @@ In fact, if you flip over the Leonardo, you'll discover additional white silkscr You can access these by specifying `A6` - `A11` in your code. For example: -{% highlight cpp %} +```cpp int analogVal = analogRead(A6); // A6 is same as D4 -{% endhighlight cpp %} +``` Another view of the back of the Arduino Leonardo board showing the additional analog input pins. @@ -360,7 +360,7 @@ Just like with our [button](buttons.md) lesson, let's walk through how one might Let's first introduce a simple program to read and print analog input values to Serial. This will provide a convenient way to test our input circuits. -{% highlight cpp %} +```cpp void setup() { Serial.begin(9600); // for printing values to console @@ -372,7 +372,7 @@ void loop() Serial.println(potVal); // print value to Serial delay(50); // Reading new values at ~20Hz } -{% endhighlight cpp %} +``` ### Building an initial circuit diff --git a/arduino/rgb-led-fade.md b/arduino/rgb-led-fade.md index b3b2bb68..a3a0b968 100644 --- a/arduino/rgb-led-fade.md +++ b/arduino/rgb-led-fade.md @@ -66,20 +66,20 @@ Our particular crossfade method works by **increasing** one LED color value (fro More specifically, we have an array `int _rgbLedValues[3]` that stores our `{int red, int green, int blue}` values. We initialize the array to `{255, 0, 0}`—so `red=255`, `green=0`, and `blue=0`. So, our RGB LED will start red. -{% highlight C %} +```cpp int _rgbLedValues[] = {255, 0, 0}; // Red, Green, Blue -{% endhighlight C %} +``` To help index into this array and track state, we create the following `enum`: -{% highlight C %} +```cpp enum RGB{ RED, GREEN, BLUE, NUM_COLORS }; -{% endhighlight C %} +``` This enum allows us to access our RGB LED values by writing `_rgbLedValues[RED]`, `_rgbLedValues[GREEN]`, and `_rgbLedValues[BLUE]` rather than `_rgbLedValues[0]`, `_rgbLedValues[1]`, and `_rgbLedValues[2]`. The enum doesn't just improve code readability and help avoid needless array index errors, it's also used to track state with two state-tracking variables: `_curFadingUpColor` and `_curFadingDownColor`. @@ -89,7 +89,7 @@ Once we reach our maximum color value of `255` for the current `_curFadingUpColo The full fade algorithm is captured in `loop()`: -{% highlight C %} +```cpp // Code based on https://gist.github.com/jamesotron/766994 (no longer available) void loop() { @@ -126,7 +126,7 @@ void loop() { setColor(_rgbLedValues[RED], _rgbLedValues[GREEN], _rgbLedValues[BLUE]); delay(DELAY_MS); } -{% endhighlight C %} +``` We control the fade step—the *amount* to fade on each `loop()` iteration—with `const int FADE_STEP`. With `FADE_STEP=1`, we fade between 768 color combinations (`3*256`). By default, `FADE_STEP=5`, which results in 156 color combinations. @@ -176,7 +176,7 @@ A screen recording of [Hunor Marton's HSL Color Picker](https://codepen.io/Hunor In our case, we perform this HSL-to-RGB conversion using the [RGBConverter](https://github.com/ratkins/RGBConverter) library. With this HSL approach, our code is comparatively much simpler, something like the following pseudocode: -{% highlight C %} +```cpp // Basic overview of our approach (pseudocode) float hue = 0, saturation = 0.8, lightness = 1.0; float hueStepValue = 0.1f; // increment hue but keep saturation and lightness fixed @@ -189,7 +189,7 @@ loop(){ hue = 0; } } -{% endhighlight C %} +``` The downside of this implementation is that we must use [`floats`](https://www.arduino.cc/en/pmwiki.php?n=Reference/Float) because the [RGBConverter](https://github.com/ratkins/RGBConverter) library uses floating point functions. Why are floats bad? Two reasons: with the ATmega328 microcontroller, floating point arithmetic is **slow** (`float` division can be 2-4 times slower than `integer` division) and **[imprecise](https://www.arduino.cc/en/pmwiki.php?n=Reference/Float)** (floats can appear infinitely precise given their use of decimals but on the ATmega328, floats have ~6-7 decimal digits of precision). @@ -240,7 +240,7 @@ Well, it turns out this fundamental feature has a long, sordid history in the Ar **First** and easiest, place all `.h` and `.cpp` files in your root sketch folder (where your `.ino` file resides): -``` +```text CrossFadeHue |-CrossFadeHue.ino |-RGBConverter.cpp @@ -249,7 +249,7 @@ CrossFadeHue **Second**, place all `.h` and `.cpp` files in a sub-folder off or your root sketch folder with a dir name of your choosing (*e.g.,* `lib`): -``` +```text CrossFadeHue |-CrossFadeHue.ino |-lib @@ -259,7 +259,7 @@ CrossFadeHue **Third**, if you have lots of `.h` and `.cpp` files and want to organize them into their own individual sub-folders, then... this can be frustrating! But there is a solution since the ~Arduino 1.6 release: you must put these sub-folders into a sub-folder called `src` ([link](https://github.com/arduino/Arduino/issues/4936#issuecomment-312953260)) within your root sketch directory. Indeed, this is exactly our setup for using the [RGBConverter](https://github.com/ratkins/RGBConverter) library. It's in `CrossFadeHue\src\RGBConverter`. So, your directory structure should look like: -``` +```text CrossFadeHue |-CrossFadeHue.ino |-src diff --git a/arduino/rgb-led.md b/arduino/rgb-led.md index 97ecf4a3..d4bc2b86 100644 --- a/arduino/rgb-led.md +++ b/arduino/rgb-led.md @@ -82,18 +82,18 @@ And here's the wiring with a breadboard (the schematic on the right is the same We are going to write code that flashes through a sequence of colors. Recall that the embedded red LED is hooked up to Pin 6, the blue LED to Pin 5, and the green LED to Pin 3. We will control the RGB LED color by outputting `HIGH` (5V) or `LOW` (0V) using [`digitalWrite` ](https://www.arduino.cc/reference/en/language/functions/digital-io/digitalwrite/) to these pins. For example, to make the RGB LED turn red, we would write: -{% highlight C %} +```cpp digitalWrite(RGB_RED_LED_PIN, HIGH); digitalWrite(RGB_GREEN_LED_PIN, LOW); digitalWrite(RGB_BLUE_LED_PIN, LOW); -{% endhighlight C %} +``` Similarly, to make the RGB LED turn green, we would write: -{% highlight C %} +```cpp digitalWrite(RGB_RED_LED_PIN, LOW); digitalWrite(RGB_GREEN_LED_PIN, HIGH); digitalWrite(RGB_BLUE_LED_PIN, LOW); -{% endhighlight C %} +``` In this example, we will flash the following sequence: @@ -117,7 +117,7 @@ blue by #0000FF, and As usual, we introduce some constants for our literal assignments and then setup our pins as `OUTPUT`. -{% highlight C %} +```cpp const int DELAY_MS = 1000; // delay between color changes in ms const int RGB_RED_LED_PIN = 6; // indicated by orange wire const int RGB_GREEN_LED_PIN = 5; // indicated by green wire @@ -130,26 +130,26 @@ void setup() pinMode(RGB_BLUE_LED_PIN, OUTPUT); pinMode(RGB_GREEN_LED_PIN, OUTPUT); } -{% endhighlight C %} +``` #### Step 2: Write a new helper function called setRgbLedColor To help set RGB LED colors, we are going to write a new function called `setRgbLedColor(int red, int green, int blue)`, which takes in either a `HIGH` or `LOW` for the red, green, and blue int parameters. -{% highlight C %} +```cpp // Function expects either HIGH or LOW for each parameter void setRgbLedColor(int red, int green, int blue){ digitalWrite(RGB_RED_LED_PIN, red); digitalWrite(RGB_GREEN_LED_PIN, green); digitalWrite(RGB_BLUE_LED_PIN, blue); } -{% endhighlight C %} +``` #### Step 3: Write the color sequence Now, in `loop()`, we'll write the specific color sequence that we want: -{% highlight C %} +```cpp void loop() { // red @@ -176,7 +176,7 @@ void loop() setRgbLedColor(HIGH, HIGH, HIGH); delay(DELAY_MS); } -{% endhighlight C %} +``` #### Step 4: Compile, upload, and run @@ -223,18 +223,18 @@ And here's the more practical breadboarded version (again, the circuit diagram i The Common Anode RGB LED works **opposite** to the Common Cathode version—to turn on a particular color, we write out `LOW` rather than `HIGH`. For example, to make the RGB LED turn red, we would write: -{% highlight C %} +```cpp digitalWrite(RGB_RED_LED_PIN, LOW); digitalWrite(RGB_GREEN_LED_PIN, HIGH); digitalWrite(RGB_BLUE_LED_PIN, HIGH); -{% endhighlight C %} +``` Similarly, to make the RGB LED turn green, we would write: -{% highlight C %} +```cpp digitalWrite(RGB_RED_LED_PIN, HIGH); digitalWrite(RGB_GREEN_LED_PIN, LOW); digitalWrite(RGB_BLUE_LED_PIN, HIGH); -{% endhighlight C %} +``` We will flash the same sequence as before but again our `HIGH`s and `LOW`s are flipped: diff --git a/arduino/serial-print.md b/arduino/serial-print.md index 8a055f7b..6d35b525 100644 --- a/arduino/serial-print.md +++ b/arduino/serial-print.md @@ -66,19 +66,19 @@ To use the serial port, we must first initialize it with [`Serial.begin(BAUD_RAT Typically, we initialize the serial port in `setup()`, as it only needs to run one time. -{% highlight C %} +```cpp void setup() { Serial.begin(9600); // opens serial port, sets data rate to 9600 bps } void loop() {} -{% endhighlight C %} +``` #### Step 2: Use Serial.print and Serial.println to write data Here's a complete program that writes "Hello world!" once every 500 ms. -{% highlight C %} +```cpp void setup() { Serial.begin(9600); // opens serial port, sets data rate to 9600 bps } @@ -87,7 +87,7 @@ void loop() { Serial.println("Hello world!"); delay(500); } -{% endhighlight C %} +``` #### Step 3: Open 'Serial Monitor' in the Arduino IDE @@ -109,7 +109,7 @@ The simple answer is to use multiple `Serial.print` and `Serial.println` stateme Below, we've written a simple program to print out the current time (in milliseconds) since the Arduino was turned on and our program began to run: -{% highlight C %} +```cpp void setup() { Serial.begin(9600); // opens serial port, sets data rate to 9600 bps } @@ -123,7 +123,7 @@ void loop() { Serial.println(" ms"); delay(500); } -{% endhighlight C %} +``` ![Serial Monitor showing timestamp output from the Arduino](assets/images/SerialPrintTimeStamp_ArduinoSerialMonitorScreenshot.png) @@ -135,7 +135,7 @@ This code is also on GitHub [here](https://github.com/makeabilitylab/arduino/blo Now, let's return to our [blink code](led-blink.md) and modify it to use `Serial.print` to print out when the LED is on and off. -{% highlight C++ %} +```cpp const int LED_OUTPUT_PIN = 3; void setup() { @@ -155,7 +155,7 @@ void loop() { Serial.println("Pin 3 is LOW (0V)"); // print status delay(500); // delay is in milliseconds } -{% endhighlight C++ %} +``` Here's a video of my code running with the Serial Monitor in the background. @@ -175,7 +175,7 @@ On the Arduino Uno and Leonardo, the built-in LED is on Pin 13. So, if you write In fact, the official [Arduino Blink example](https://www.arduino.cc/en/Tutorial/Blink) uses the built-in LED and the constant `LED_BUILTIN` to demonstrate blinking. This is also the program that ships with your Arduino and runs when you first power it up. -{% highlight C %} +```cpp // the setup function runs once when you press reset or power the board void setup() { // initialize digital pin LED_BUILTIN as an output. @@ -189,7 +189,7 @@ void loop() { digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW delay(1000); // wait for a second } -{% endhighlight C %} +``` You can access this example directly in the Arduino IDE: @@ -205,7 +205,7 @@ A third approach, which also uses serial, is called [**Serial Plotter**](https:/ Let's make a new program that steadily increases a state value called `_triangleValue`. Once it passes a certain threshold `TURN_ON_THRESHOLD`, we turn on the LED at `LED_BUILTIN`. -{% highlight C %} +```cpp const int LED_OUTPUT_PIN = LED_BUILTIN; const int MAX_THRESHOLD = 255; const int MIN_THRESHOLD = 0; @@ -236,7 +236,7 @@ void loop() { delay(30); } -{% endhighlight C %} +``` To open the Serial Plotter, go to `Tools -> Serial Plotter` or click on the "graph icon" in the Arduino IDE toolbar. Here's a video of this code running with the Serial Plotter showing a triangle wave. @@ -250,13 +250,13 @@ To open the Serial Plotter, go to `Tools -> Serial Plotter` or click on the "gra The Arduino IDE 2 Serial Plotter supports **multiple named data lines** with auto-generated legends and distinct colors. To use this feature, format your output as comma-separated `Label:value` pairs, ending with a newline. For example: -``` +```text Label1:value1,Label2:value2\n ``` Let's modify our previous program but graph the `TURN_ON_THRESHOLD`. -{% highlight C %} +```cpp const int LED_OUTPUT_PIN = LED_BUILTIN; const int MAX_THRESHOLD = 255; const int MIN_THRESHOLD = 0; @@ -291,7 +291,7 @@ void loop() { delay(30); } -{% endhighlight C %} +``` In the Serial Plotter, you'll see two colored lines with a legend: `Turn-on Threshold` as a solid line at the `TURN_ON_THRESHOLD` value and `Triangle` as a triangular ramp of `_triangleValue`. The IDE automatically assigns colors and generates the legend from your labels. You can click on the checkboxes to toggle which values are graphed. @@ -308,7 +308,7 @@ In the Serial Plotter, you'll see two colored lines with a legend: `Turn-on Thre Once you learn [`analogRead`](https://www.arduino.cc/reference/en/language/functions/analog-io/analogread/) in a [future lesson](potentiometers.md), the Serial Plotter becomes especially powerful for visualizing real sensor data. For example, you could plot a sensor reading alongside a threshold value and use the built-in LED to indicate when the threshold is crossed: -{% highlight C %} +```cpp const int SENSOR_PIN = A0; const int THRESHOLD = 800; @@ -336,7 +336,7 @@ void loop() { delay(50); } -{% endhighlight C %} +``` The Serial Plotter shows the sensor value fluctuating in real time with a flat threshold line for comparison, and the built-in LED lights up whenever the value goes above the threshold—giving you both visual feedback on the board *and* a graph on screen. This combination of Serial Plotter + indicator LED is a technique you'll use frequently when calibrating sensors and tuning interactive projects. diff --git a/arduino/tone.md b/arduino/tone.md index 822d4ffe..aaeab0c5 100644 --- a/arduino/tone.md +++ b/arduino/tone.md @@ -99,7 +99,7 @@ As you turn the potentiometer, listen carefully: in the `analogWrite()` mode, th **Figure.** A Tinkercad Circuits simulation demonstrating that `analogWrite` changes the duty cycle (visible on the oscilloscope) but not the frequency—so the buzzer pitch remains constant. {: .fs-1 } --> - @@ -142,11 +142,11 @@ Compare this with `tone()`, where the duty cycle is always 50% but you control t Arduino provides three functions for generating tones: -{% highlight cpp %} +```cpp tone(pin, frequency) // play continuously until noTone() is called tone(pin, frequency, duration) // play for 'duration' milliseconds, then stop noTone(pin) // stop playing -{% endhighlight cpp %} +``` A few important details: @@ -239,7 +239,7 @@ To let you interactively explore the difference, we built this p5js sound visual Let's start by playing a single tone. The following code plays concert A (440 Hz) for one second, pauses for half a second, then repeats. You can [play with it interactively on Tinkercad](https://www.tinkercad.com/things/aqUoDUiMU7x-simple-tone) or copy/paste it into the Arduino IDE and run it for real! -{% highlight cpp %} +```cpp const int BUZZER_PIN = 9; void setup() { @@ -252,7 +252,7 @@ void loop() { noTone(BUZZER_PIN); // Stop tone delay(500); // Pause for half a second } -{% endhighlight cpp %} +``` Try changing the frequency: 262 is middle C, 523 is one octave higher (C5), and 1000 produces a high-pitched tone. What's the lowest frequency you can hear? The highest? (Most humans can hear roughly 20 Hz to 20 kHz, but this varies with age.) Note: The standard Arduino `tone()` function has a minimum frequency of 31 Hz due to hardware timer limitations, so you won't be able to test the absolute bottom of human hearing (20 Hz). @@ -269,7 +269,7 @@ Now let's play something more musical. The Arduino IDE ships with a helpful file Here are a few of the note definitions from [`pitches.h`](https://github.com/arduino/arduino-examples/blob/main/examples/02.Digital/toneMelody/pitches.h). You can also find musical note frequencies in this [Piano Key Frequencies article](https://en.wikipedia.org/wiki/Piano_key_frequencies) on Wikipedia. -{% highlight cpp %} +```cpp #define NOTE_C4 262 // Middle C #define NOTE_D4 294 #define NOTE_E4 330 @@ -278,11 +278,11 @@ Here are a few of the note definitions from [`pitches.h`](https://github.com/ard #define NOTE_A4 440 // Concert A #define NOTE_B4 494 #define NOTE_C5 523 // C one octave above middle C -{% endhighlight cpp %} +``` Using these constants, we can play a C major scale: -{% highlight cpp %} +```cpp #define NOTE_C4 262 // Middle C #define NOTE_D4 294 #define NOTE_E4 330 @@ -323,7 +323,7 @@ void loop() { delay(1000); // pause before repeating } -{% endhighlight cpp %} +``` Notice that we use the `duration` parameter of `tone()` here, so we don't need to call `noTone()` manually—the tone stops automatically after `NOTE_DURATION_MS` milliseconds. One subtlety: `tone()` is **non-blocking**, meaning the sketch continues executing immediately even while the tone is still playing. That's why we still need the `delay()` call—without it, the loop would race ahead to the next note before the current one finishes. @@ -346,7 +346,7 @@ The Arduino IDE includes a built-in example that plays a short melody. You can a Instead, we've written our own version using the Imperial March from Star Wars. You can [try it on Tinkercad here](https://www.tinkercad.com/things/l2d7xFFuWFY/). -{% highlight cpp %} +```cpp #define NOTE_C4 262 #define NOTE_D4 294 #define NOTE_E4 330 @@ -419,7 +419,7 @@ void setup() { void loop() { // Melody plays once in setup(), nothing to do here } -{% endhighlight cpp %} +``` To represent a rest (silence), use a note value of `0` in the melody array. The code checks for this and simply skips the `tone()` call, relying on the `delay()` to produce a silent pause. Avoid calling `tone(pin, 0)` directly—while it happens to produce silence on AVR boards, it causes crashes on other platforms (like SAMD) due to a division by zero in the timer math. @@ -448,7 +448,7 @@ The simplest approach is to turn an LED on while a note plays and off during the **Figure.** A Tinkercad Circuits simulation of a simple siren with an LED that toggles with each tone change. Try it yourself in [Tinkercad](https://www.tinkercad.com/things/frb7eeyVkKN-simple-siren-with-external-led-no-breadboard/)! {: .fs-1 } -{% highlight cpp %} +```cpp const int BUZZER_PIN = 9; const int LED_PIN = 2; const int SOUND_DURATION_MS = 500; @@ -469,7 +469,7 @@ void loop() { digitalWrite(LED_PIN, LOW); // LED off for low tone delay(SOUND_DURATION_MS); } -{% endhighlight cpp %} +```