Eloquent library – Eloquent Arduino Blog http://eloquentarduino.github.io/ Machine learning on Arduino, programming & electronics Wed, 16 Dec 2020 20:29:52 +0000 en-US hourly 1 https://wordpress.org/?v=5.3.6 Esp32-cam motion detection WITH PHOTO CAPTURE! (grayscale version) https://eloquentarduino.github.io/2020/12/esp32-cam-motion-detection-with-photo-capture-grayscale-version/ Thu, 03 Dec 2020 17:50:59 +0000 https://eloquentarduino.github.io/?p=1390 Do you want to transform your cheap esp32-cam in a DIY surveillance camera with moton detection AND photo capture? Look no further: this post explains STEP-BY-STEP all you need to know to build one yourself! As I told you in the Easier, faster pure video Esp32-cam motion detection post, motion detection on the esp32-cam seems […]

L'articolo Esp32-cam motion detection WITH PHOTO CAPTURE! (grayscale version) proviene da Eloquent Arduino Blog.

]]>
Do you want to transform your cheap esp32-cam in a DIY surveillance camera with moton detection AND photo capture?

Look no further: this post explains STEP-BY-STEP all you need to know to build one yourself!

Esp32-cam motion detection

As I told you in the Easier, faster pure video Esp32-cam motion detection post, motion detection on the esp32-cam seems to be the hottest topic on my blog, so I thought it deserved some more tutorials.

Without question, to #1 request you made me in the comments was

How can I save the image that triggered the motion detection to the disk?

Well, in this post I will show you how to save the image to the SPIFFS filesystem your esp32-cam comes equipped with!

Motion detection, refactored

Please read the post on easier, faster esp32-cam motion detection first if you want to understand the following code.

It took me quite some time to write this post because I was struggling to design a clear, easy to use API for the motion detection feature and the image storage.

And I have to admit that, even after so long, I'm still not satisfied with the results.

Nonetheless, it works, and it works well in my opinion, so I will publish this and maybe get feedback from you to help me improve (so please leave a comment if you have any suggestion).

I won't bother you with the design considerations I took since this is an hands-on tutorial, so let's take a look at the code to implement motion detection on the esp32-cam or any other esp32 with a camera attached (I'm using the M5Stick camera).

First of all, you need the EloquentVision library: you can install it either from Github or using the Arduino IDE's Library Manager.

Next, the code.

// Change according to your model
// The models available are
//   - CAMERA_MODEL_WROVER_KIT
//   - CAMERA_MODEL_ESP_EYE
//   - CAMERA_MODEL_M5STACK_PSRAM
//   - CAMERA_MODEL_M5STACK_WIDE
//   - CAMERA_MODEL_AI_THINKER
#define CAMERA_MODEL_M5STACK_WIDE

#include <FS.h>
#include <SPIFFS.h>
#include "EloquentVision.h"

// set the resolution of the source image and the resolution of the downscaled image for the motion detection
#define FRAME_SIZE FRAMESIZE_QVGA
#define SOURCE_WIDTH 320
#define SOURCE_HEIGHT 240
#define CHANNELS 1
#define DEST_WIDTH 32
#define DEST_HEIGHT 24
#define BLOCK_VARIATION_THRESHOLD 0.3
#define MOTION_THRESHOLD 0.2

// we're using the Eloquent::Vision namespace a lot!
using namespace Eloquent::Vision;
using namespace Eloquent::Vision::IO;
using namespace Eloquent::Vision::ImageProcessing;
using namespace Eloquent::Vision::ImageProcessing::Downscale;
using namespace Eloquent::Vision::ImageProcessing::DownscaleStrategies;

// an easy interface to capture images from the camera
ESP32Camera camera;
// the buffer to store the downscaled version of the image
uint8_t resized[DEST_HEIGHT][DEST_WIDTH];
// the downscaler algorithm
// for more details see https://eloquentarduino.github.io/2020/05/easier-faster-pure-video-esp32-cam-motion-detection
Cross<SOURCE_WIDTH, SOURCE_HEIGHT, DEST_WIDTH, DEST_HEIGHT> crossStrategy;
// the downscaler container
Downscaler<SOURCE_WIDTH, SOURCE_HEIGHT, CHANNELS, DEST_WIDTH, DEST_HEIGHT> downscaler(&crossStrategy);
// the motion detection algorithm
MotionDetection<DEST_WIDTH, DEST_HEIGHT> motion;

void setup() {
    Serial.begin(115200);
    SPIFFS.begin(true);
    camera.begin(FRAME_SIZE, PIXFORMAT_GRAYSCALE);
    motion.setBlockVariationThreshold(BLOCK_VARIATION_THRESHOLD);
}

void loop() {
    camera_fb_t *frame = camera.capture();

    // resize image and detect motion
    downscaler.downscale(frame->buf, resized);
    motion.update(resized);
    motion.detect();

    if (motion.ratio() > MOTION_THRESHOLD) {
        Serial.println("Motion detected");

        // here we want to save the image to disk
    }
}

Save image to disk

Fine, we can detect motion!

Now we want to save the triggering image to disk in a format that we can decode without any custom software. It would be cool if we could see the image using the native Esp32 Filesystem Browser sketch.

Thankfully to the guys at espressif, the esp32 is able to encode a raw image to JPEG format: it is convenient to use (any PC on earth can read a jpeg) and it is also fast.

and thanks to the reader ankaiser for pointing it out

It's really easy to do thanks to the EloquentVision library.

if (motion.ratio() > MOTION_THRESHOLD) {
        Serial.println("Motion detected");

        // quality ranges from 10 to 64 -> the higher, the more detailed
        uint8_t quality = 30;
        JpegWriter<SOURCE_WIDTH, SOURCE_HEIGHT> jpegWriter;
        File imageFile = SPIFFS.open("/capture.jpg", "wb");

        // it takes < 1 second for a 320x240 image and 4 Kb of space
        jpegWriter.writeGrayscale(imageFile, frame->buf, quality);
        imageFile.close();
}

Well done! Now your image is on the disk and can be downloaded with the FSBrowser sketch.

Now you have all the tools you need to create your own DIY surveillance camera with motion detection feature!

You can use it to catch thieves (I discourage you to rely on such a rudimentary setup however!), to capture images of wild animals in your garden (birds, sqirrels or the like), or any other application you see fit.

Further improvements

Of course you may well understand that a proper motion detection setup should be more complex than the one presented here. Nevertheless, a couple of quick fixes can greatly improve the usability of this project with little effort. Here I suggest you a couple.

#1: Debouncing successive frames: the code presented in this post is a stripped down version of a more complete esp32-cam motion detection example sketch.

That sketch implements a debouncing function to prevent writing "ghost images" (see the original post on motion detection for a clear evidence of this effect).

#2: Proper file naming: the example sketch uses a fixed filename for the image. This means any new image will overwrite the older, which may be undesiderable based on your requirements. A proper way to handle this would be to attach an RTC and name the image after the time it occurred (something like "motion_2020-12-03_08:09:10.bmp")

#3: RGB images: this is something I'm working on. I mean, the Bitmap writer is there (so you could actually use it to store images on your esp32), but the multi-channel motion detection is driving me crazy, I need some more time to design it the way I want, so stay tuned!


I hope you enjoyed this tutorial on esp32-cam motion detection with photo capture: it was born as a response to your asking, so don't be afraid and ask me anything: I will do my best to help you!

L'articolo Esp32-cam motion detection WITH PHOTO CAPTURE! (grayscale version) proviene da Eloquent Arduino Blog.

]]>
Easy ESP32 camera HTTP video streaming server https://eloquentarduino.github.io/2020/06/easy-esp32-camera-http-video-streaming-server/ Wed, 24 Jun 2020 17:27:33 +0000 https://eloquentarduino.github.io/?p=1203 This will be a short post where I introduce a new addition to the Arduino Eloquent library aimed to make video streaming from an ESP32 camera over HTTP super easy. It will be the first component of a larger project I'm going to implement. If you Google "esp32 video streaming" you will get a bunch […]

L'articolo Easy ESP32 camera HTTP video streaming server proviene da Eloquent Arduino Blog.

]]>
This will be a short post where I introduce a new addition to the Arduino Eloquent library aimed to make video streaming from an ESP32 camera over HTTP super easy. It will be the first component of a larger project I'm going to implement.

If you Google "esp32 video streaming" you will get a bunch of results that are essentialy copy-pasted from the official Espressif repo: many of them neither copy-pasted the code, just tell you to load the example sketch.

And if you try to read it and try to modify just a bit for your own use-case, you won't understand much.

This is the exact environment for an Eloquent component to live: make it painfully easy what's messy.

I still have to find a good naming scheme for my libraries since Arduino IDE doesn't allow nested imports, so forgive me if "ESP32CameraHTTPVideoStreamingServer.h" was the best that came to mind.

How easy is it to use?

1 line of code if used in conjuction with my other library EloquentVision.

#define CAMERA_MODEL_M5STACK_WIDE
#include "WiFi.h"
#include "EloquentVision.h"
#include "ESP32CameraHTTPVideoStreamingServer.h"

using namespace Eloquent::Vision;
using namespace Eloquent::Vision::Camera;

ESP32Camera camera;
HTTPVideoStreamingServer server(81);

/**
 *
 */
void setup() {
    Serial.begin(115200);
    WiFi.softAP("ESP32", "12345678");

    camera.begin(FRAMESIZE_QVGA, PIXFORMAT_JPEG);
    server.start();

    Serial.print("Camera Ready! Use 'http://");
    Serial.print(WiFi.softAPIP());
    Serial.println(":81' to stream");
}

void loop() {
}

HTTPVideoStreamingServer assumes you already initialized your camera. You can achieve this task in the way you prefer: ESP32Camera class makes this a breeze.

81 in the server constructor is the port you want the server to be listening to.

Once connected to WiFi or started in AP mode, all you have to do is call start(): that's it!

Finding this content useful?

What else is it good for?

The main reason I wrote this piece of library is because one of you reader commented on the motion detection post asking if it would be possible to start the video streaming once motion is detected.

Of course it is.

It's just a matter of composing the Eloquent pieces.

// not workings AS-IS, needs refactoring

#define CAMERA_MODEL_M5STACK_WIDE
#include "WiFi.h"
#include "EloquentVision.h"
#include "ESP32CameraHTTPVideoStreamingServer.h"

#define FRAME_SIZE FRAMESIZE_QVGA
#define SOURCE_WIDTH 320
#define SOURCE_HEIGHT 240
#define CHANNELS 1
#define DEST_WIDTH 32
#define DEST_HEIGHT 24
#define BLOCK_VARIATION_THRESHOLD 0.3
#define MOTION_THRESHOLD 0.2

// we're using the Eloquent::Vision namespace a lot!
using namespace Eloquent::Vision;
using namespace Eloquent::Vision::Camera;
using namespace Eloquent::Vision::ImageProcessing;
using namespace Eloquent::Vision::ImageProcessing::Downscale;
using namespace Eloquent::Vision::ImageProcessing::DownscaleStrategies;

ESP32Camera camera;
HTTPVideoStreamingServer server(81);
// the buffer to store the downscaled version of the image
uint8_t resized[DEST_HEIGHT][DEST_WIDTH];
// the downscaler algorithm
// for more details see https://eloquentarduino.github.io/2020/05/easier-faster-pure-video-esp32-cam-motion-detection
Cross<SOURCE_WIDTH, SOURCE_HEIGHT, DEST_WIDTH, DEST_HEIGHT> crossStrategy;
// the downscaler container
Downscaler<SOURCE_WIDTH, SOURCE_HEIGHT, CHANNELS, DEST_WIDTH, DEST_HEIGHT> downscaler(&crossStrategy);
// the motion detection algorithm
MotionDetection<DEST_WIDTH, DEST_HEIGHT> motion;

/**
 *
 */
void setup() {
    Serial.begin(115200);
    WiFi.softAP("ESP32", "12345678");

    camera.begin(FRAMESIZE_QVGA, PIXFORMAT_GRAYSCALE);
    motion.setBlockVariationThreshold(BLOCK_VARIATION_THRESHOLD);

    Serial.print("Camera Ready! Use 'http://");
    Serial.print(WiFi.softAPIP());
    Serial.println(":81' to stream");
}

void loop() {
    camera_fb_t *frame = camera.capture();

    // resize image and detect motion
    downscaler.downscale(frame->buf, resized);
    motion.update(resized);
    motion.detect();

    if (motion.ratio() > MOTION_THRESHOLD) {
        Serial.print("Motion detected");
        // start the streaming server when motion is detected
        // shutdown after 20 seconds if no one connects
        camera.begin(FRAMESIZE_QVGA, PIXFORMAT_JPEG);
        delay(2000);
        Serial.print("Camera Server ready! Use 'http://");
        Serial.print(WiFi.softAPIP());
        Serial.println(":81' to stream");
        server.start();
        delay(20000);
        server.stop();
        camera.begin(FRAMESIZE_QVGA, PIXFORMAT_GRAYSCALE);
        delay(2000);
    }

    // probably we don't need 30 fps, save some power
    delay(300);
}

Does it look good?

Now the rationale behind Eloquent components should be starting to be clear to you: easy to use objects you can compose the way it fits to achieve the result you want.

Would you suggest me more piece of functionality you would like to see wrapped in an Eloquent component?


You can find the class code and the example sketch on the Github repo.

L'articolo Easy ESP32 camera HTTP video streaming server proviene da Eloquent Arduino Blog.

]]>
Easy Arduino thermal camera with (ASCII) video streaming https://eloquentarduino.github.io/2020/02/easy-arduino-thermal-camera-with-ascii-video-streaming/ Sat, 29 Feb 2020 16:20:15 +0000 https://eloquentarduino.github.io/?p=956 Ever wanted to use your thermal camera with Arduino but found it difficult to go beyond the tutorials code? Let's see the easiest possible way to view your thermal camera streaming without an LCD display! MLX90640 thermal camera For Arduino there are essentially two thermal camera available: the AMG8833 and the MLX90640. The AMG8833 is […]

L'articolo Easy Arduino thermal camera with (ASCII) video streaming proviene da Eloquent Arduino Blog.

]]>
Ever wanted to use your thermal camera with Arduino but found it difficult to go beyond the tutorials code? Let's see the easiest possible way to view your thermal camera streaming without an LCD display!

Arduino thermal image rgb vs ascii

MLX90640 thermal camera

For Arduino there are essentially two thermal camera available: the AMG8833 and the MLX90640.

The AMG8833 is 8x8 and the MLX90640 is 32x24.

They're not cheap, it is true.

But if you have to spend money, I strongly advise you to buy the MLX90640: I have one and it's not that accurate. I can't imagine how low definition would be the AMG8833.

If you want to actually get something meaningful from the camera, the AMG8833 won't give you any good results.

Sure, you can do interpolation: interpolation would give you the impression you have a better definition, but you're just "inventing" values you don't actually have.

For demo projects it could be enough. But for any serious application, spend 20$ more and buy an MLX90640.

MLX90640 eloquent library

As you may know if you read my previous posts, I strongly believe in "eloquent" code, that is code that's as easy as possible to read.

How many lines do you think you need to read a MLX90640 camera? Well, not that much in fact.

#include "EloquentMLX90640.h"

using namespace Eloquent::Sensors;

float buffer[768];
MLX90640 camera;

void setup() {
  Serial.begin(115200);

  if (!camera.begin()) {
    Serial.println("Init error");
    delay(50000);
  }
}

void loop() {
  camera.read(buffer);
  delay(3000);
}

If you skip the declaration lines, you only need a begin() and read() call.

That's it.

What begin() does is to run all of the boilerplate code I mentioned earlier (checking the connection and initializing the parameters).

read() populates the buffer you pass as argument with the temperature readings.

From now on, you're free to handle that array as you may like: this is the most flexible way for the library to handle any use-case. It simply does not pose any restriction.

You can find the camera code at the end of the page or on Github.

Printing as ASCII Art

Now that you have this data, you may want to actually "view" it. Well, that's not an easy task as one may hope.

You will need an LCD if you want to create a standalone product. If you have one, it'll be the best, it's a really cute project to build.

Here's a video from Adafruit that showcases even a 3D-printed case.

If you don't have an LCD, though, it is less practical to access your image.

I did this in the past, and it meant creating a Python script reading the serial port every second and updating a plot.
It works, sure, but it's not the most convenient way to handle it.

This is the reason I thought about ASCII art: it is used to draw images in plain text, so you can view them directly in the serial monitor.

Of course they will not be as accurate or representative as RGB images, but can give you an idea of what you're framing in realtime.

I wrote a class to do this. Once imported in your sketch, it is super easy to get it working.

#include "EloquentAsciiArt.h"

using namespace Eloquent::ImageProcessing;

float buffer[768];
uint8_t bufferBytes[768];
MLX90640 camera;
// we need to specify width and height of the image
AsciiArt<32, 24> art(bufferBytes);

void loop() {
  camera.read(buffer);

  // convert float image to uint8
  for (size_t i = 0; i < 768; i++) {
    // assumes readings are in the range 0-40 degrees
    // change as per your need
    bufferBytes[i] = map(buffer[i], 0, 40, 0, 255);
  }

  // print to Serial with a border of 2 characters, to distinguish one image from the next
  art.print(&Serial, 2);
  delay(2000);
}

As you can see, you need to create an AsciiArt object, map the image pixels in the range 0-255 and call the print() method: easy peasy!

You can find the ASCII art generator code at the end of the page or on Github.

Here's the result of the sketch. It's a video of me putting my arms at the top of my head, once at a time, then standing up.

Resize the Serial Monitor as only a single frame at a time is visble to have a "video streaming" effect

Of course the visual effect won't be as impressive as an RGB image, but you can clearly see my figure moving.

The real bad part is the "glitch" you see between each frame when the scrolling happens: this is something I don't know if it's possible to mitigate.


Check the full project code on Github


#pragma once

#include "Wire.h"
#include "MLX90640_API.h"
#include "MLX90640_I2C_Driver.h"

#ifndef TA_SHIFT
//Default shift for MLX90640 in open air
#define TA_SHIFT 8
#endif

namespace Eloquent {
    namespace Sensors {

        enum class MLX90640Status {
            OK,
            NOT_CONNECTED,
            DUMP_ERROR,
            PARAMETER_ERROR,
            FRAME_ERROR
        };

        class MLX90640 {
        public:
            /**
             *
             * @param address
             */
            MLX90640(uint8_t address = 0x33) :
                _address(address),
                _status(MLX90640Status::OK) {

            }

            /**
             *
             * @return
             */
            bool begin() {
                Wire.begin();
                Wire.setClock(400000);

                return isConnected() && loadParams();
            }

            /**
             *
             * @return
             */
            bool read(float result[768]) {
                for (byte x = 0 ; x < 2 ; x++) {
                    uint16_t frame[834];
                    int status = MLX90640_GetFrameData(_address, frame);

                    if (status < 0)
                        return fail(MLX90640Status::FRAME_ERROR);

                    float vdd = MLX90640_GetVdd(frame, &_params);
                    float Ta = MLX90640_GetTa(frame, &_params);
                    float tr = Ta - TA_SHIFT;
                    float emissivity = 0.95;

                    MLX90640_CalculateTo(frame, &_params, emissivity, tr, result);
                }
            }

        protected:
            uint8_t _address;
            paramsMLX90640 _params;
            MLX90640Status _status;

            /**
             * Test if device is connected
             * @return
             */
            bool isConnected() {
                Wire.beginTransmission(_address);

                if (Wire.endTransmission() == 0) {
                    return true;
                }

                return fail(MLX90640Status::NOT_CONNECTED);
            }

            /**
             *
             * @return
             */
            bool loadParams() {
                uint16_t ee[832];
                int status = MLX90640_DumpEE(_address, ee);

                if (status != 0)
                    return fail(MLX90640Status::DUMP_ERROR);

                status = MLX90640_ExtractParameters(ee, &_params);

                if (status != 0)
                    return fail(MLX90640Status::PARAMETER_ERROR);

                return true;
            }

            /**
             * Mark a failure
             * @param status
             * @return
             */
            bool fail(MLX90640Status status) {
                _status = status;

                return false;
            }
        };
    }
}
#pragma once

#include "Stream.h"

namespace Eloquent {
    namespace ImageProcessing {

        /**
         *
         * @tparam width
         * @tparam height
         */
        template<size_t width, size_t height>
        class AsciiArt {
        public:
            AsciiArt(const uint8_t *data) {
                _data = data;
            }

            /**
             * Get pixel at given coordinates
             * @param x
             * @param y
             * @return
             */
            uint8_t at(size_t x, size_t y) {
                return _data[y * width + x];
            }

            /**
             * Print as ASCII art picture
             * @param stream
             */
            void print(Stream *stream, uint8_t frameSize = 0) {
                const char glyphs[] = " .,:;xyYX";
                const uint8_t glyphsCount = 9;

                printAsciiArtHorizontalFrame(stream, frameSize);

                for (size_t y = 0; y < height; y++) {
                    // vertical frame
                    for (uint8_t k = 0; k < frameSize; k++)
                        Serial.print('|');

                    for (size_t x = 0; x < width; x++) {
                        const uint8_t glyph = floor(((uint16_t) at(x, y)) * glyphsCount / 256);

                        stream->print(glyphs[glyph]);
                    }

                    // vertical frame
                    for (uint8_t k = 0; k < frameSize; k++)
                        Serial.print('|');

                    stream->print('\n');
                }

                printAsciiArtHorizontalFrame(stream, frameSize);
                stream->flush();
            }

        protected:
            const uint8_t *_data;

            /**
             *
             * @param stream
             * @param frameSize
             */
            void printAsciiArtHorizontalFrame(Stream *stream, uint8_t frameSize) {
                for (uint8_t i = 0; i < frameSize; i++) {
                    for (size_t j = 0; j < width + 2 * frameSize; j++)
                        stream->print('-');
                    stream->print('\n');
                }
            }
        };
    }
}

L'articolo Easy Arduino thermal camera with (ASCII) video streaming proviene da Eloquent Arduino Blog.

]]>
Eloquent pin management: the Pin class https://eloquentarduino.github.io/2019/12/arduino-pin-class/ Fri, 06 Dec 2019 16:15:13 +0000 https://eloquentarduino.github.io/?p=167 Pin is a class for pin manipulation: you can read, write, turnOn, turnOff, toggle and a lot more. Please, stop writing horrible code like digitalWrite(led, HIGH) and start writing led.turnOn() instead. Pin is actually an abstract class, so you won't use it directly, but through its specialized implementations: DigitalIn DigitalOut AnalogIn AnalogOut Import the library […]

L'articolo Eloquent pin management: the Pin class proviene da Eloquent Arduino Blog.

]]>
Pin is a class for pin manipulation: you can read, write, turnOn, turnOff, toggle and a lot more. Please, stop writing horrible code like digitalWrite(led, HIGH) and start writing led.turnOn() instead.

Pin is actually an abstract class, so you won't use it directly, but through its specialized implementations:

  1. DigitalIn
  2. DigitalOut
  3. AnalogIn
  4. AnalogOut

Import the library

To follow this tutorial along you need to first install the Eloquent library
#import <eIO.h>

using namespace Eloquent::Pin;

If the namespace stuff is new to you, here I'll briefly say that it is used to avoid name collisions among different libraries. This seems to be an alien topic in the Arduino world and I can't really explain why.

99% of the libraries out there deal with this problem in one of two modes:

  1. ignoring it altogether, so you have an MPU6050.h library, which elects itself as the only one implementation possible to access the MPU6050 accelerometer in the world
  2. prefixing each library file, so you get the Adafruit_Si7021 class

The case for Adafruit_Si7021 should not exist in my opinion: use the damn namespaces!
Click To Tweet


With namespaces, it would become:

using namespace Adafruit;

Si7021 si;

How to use

First of all, all the 4 implementations accept a single constructor argument: the pin number.

 DigitalOut led(BUILTIN_LED);
 DigitalIn pushButton(10);
 AnalogIn potentiometer(A0);
 AnalogOut pwmLed(8);

Then it is good practice to init your pins in the setup.

 void setup() {
    led.begin();
    pushButton.begin();
    potentiometer.begin();
    pwmLed.begin();
 }

All the 4 classes let you ask for the pin current value via the value() method:

 void test() {
    // DigitalIn returns the last read value, as 0/1
    digitalIn.value();

    // AnalogIn returns the last read value, in the range [0, 1024]
    analogIn.value();

    // DigitalOut returns the last written value, as 0/1
    digitalOut.value();

    // AnaloglOut returns the last written value, in the range [0, 255]
    analogOut.value();
 }

At this point each class will provide its specialized methods.

DigitalIn

void test() {
    // configure pin as INPUT_PULLUP
    pin.pullup();

    // configure pin as Active Low 
    // that is, pin is ON when digitalRead() is LOW
    pin.activeLow();

    // read and update pin value
    pin.read();

    // test if pin is on (doesn't read the pin)
    pin.isOn();

    // test if pin is off (doesn't read the pin)
    pin.isOff();

    // test if pin value changed from last reading
    pin.changed();
}

DigitalOut

void test() {
    // set pin as Active Low
    // that is, turnOn writes LOW
    pin.activeLow();

    // turn pin on
    pin.turnOn();

    // turn pin off
    pin.turnOff();

    // toggle
    pin.toggle();

    // blink N times at intervals of X milliseconds
    pin.blink(N, X);
}

AnalogIn

void test() {
    // read current pin value
    pin.read();

    // get pin previous value
    pin.prev();

    // get difference between current value and previous
    pin.delta();

    // get absolute value of delta()
    pin.absDelta();
}

AnalogOut

void test() {
    // write value X to pin
    pin.write(X);
}

If you don't believe a whole class is worthwhile to work with pins, I'll show a few use cases to illustrate my point.

Use case #1: active low LED

The ESP8266 has a builtin LED you can control, but it is an active low one: it will turn on when you write LOW. In this case, digitalWrite(BUILTIN_LED, LOW) can be misleading regarding your actual intentions.

It doesn't look intuitive, it doesn't look eloquent! builtinLed.turnOn() does, however. All you need to get it working correctly is calling builtinLed.activeLow() once in your setup.

// BEFORE
void loop() {
    // to turn the builtin LED on
    digitalWrite(led, LOW);
}
// AFTER
DigitalOut buildtinLed;

void setup() {
    builtinLed.activeLow();
}

void loop() {
    // to turn the builtin LED on
    builtinLed.turnOn();
}

Use case #2: toggle

If you need to toggle the current state of a digital output, you need an helper variable to keep track of the state and remember to always update that variable when you write to the output.
With a class, the state is tightly bound to the instance, so you have a single source of truth: turnOn(), turnOff() and toggle() will take care of updating the inner state accordingly.

// BEFORE
#define LED 1

bool ledState = true;

loop() {
    digitalWrite(LED, ledState);
    ledState = !ledState
}
// AFTER
DigitalOut led(1);

void loop() {
    led.toggle();
}

Use case #3: analog delta

What if you have an analog input and want to know if its valued changed by at least X from your latest reading? You would need an helper variable again.

Now imagine if you have 5 analog inputs you want to track: you'll end up with 10 variables and of course you have again to always keep both in sync.
AnalogIn conveniently provides delta() and absDelta() methods that give you the change from the previous reading and will always be in sync. Awesome!

// BEFORE
#define INPUT1 A1
#define INPUT2 A2

uint16_t current1, prev1;
uint16_t current2, prev2;

void loop() {
    prev1 = current1;
    current1 = analogRead(INPUT1);
    prev2 = current2;
    current2 = analogRead(INPUT2);

    if (abs(current1 - prev1) > THRESHOLD)
        ...
// AFTER
AnalogIn input1(A1), input2(A2);

void loop() {
    input1.read();
    input2.read();

    if (input1.absDelta() > THRESHOLD)
        ...
}

Did you find this tutorial useful? Was is it easy to follow or did I miss something? Let me know in the comments so I can keep improving the blog.

L'articolo Eloquent pin management: the Pin class proviene da Eloquent Arduino Blog.

]]>
Eloquent bounded waiting: the await construct https://eloquentarduino.github.io/2019/12/arduino-bounded-waiting/ Thu, 05 Dec 2019 18:50:59 +0000 https://eloquentarduino.github.io/?p=211 Sometimes you may need to wait for a certain condition to become true, but you don't want to wait forever: it may be awaiting for Serial, for the Wifi to connect to a network, or the response from a SoftwareSerial peripheral. The await construct lets you put an upper bound to the time you're willing […]

L'articolo Eloquent bounded waiting: the await construct proviene da Eloquent Arduino Blog.

]]>
Sometimes you may need to wait for a certain condition to become true, but you don't want to wait forever: it may be awaiting for Serial, for the Wifi to connect to a network, or the response from a SoftwareSerial peripheral. The await construct lets you put an upper bound to the time you're willing to wait.

Most often, you see example code of this kind:

Serial.print("Attempting to connect to WiFi");

while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
}

If the connection doesn't succeed (maybe the AP is out of range or is down), you're stuck in an endless wait. A proper way for handling such situations is with a timeout that gets you out of the loop with an error status so you can handle the failure.
await is exactly this: a construct to await for a condition to become true until a timeout expires, returning true or false as a response.

Definition

#define await(condition, timeout) await_with_interval(condition, timeout, 10)
#define await_with_interval(condition, timeout, interval) \
  ([]() { \
    uint32_t start = millis(); \
    while (millis() - start <= timeout) { \
      if (condition) return true; \
      delay(interval); \
    } \
  return false; })()

How to use

await needs at least two arguments:

  1. the condition to await for
  2. the timeout, in milliseconds
// these are for greater code readability
#define Millis 
#define Second  *1000
#define Seconds *1000
bool wifiConnected = await(WiFi.status() == WL_CONNECTED, 10 Seconds)

The code above will wait 10 seconds for the wifi to connect: on failure, wifiConnected will be false and you can gently fail.

You can use it for any kind of check, like waiting for Serial.

bool serialReady = await(Serial, 5 Seconds)
bool serialHasCharacters = await(Serial.available(), 5 Seconds)

The default interval between checks is 10 milliseconds: if you need a custom delay interval you can use the more verbose await_with_interval:

// await WiFi for 10 seconds, check if connected every 500 millis
bool wifiConnected = await_with_interval(WiFi.status() == WL_CONNECTED, 10 Seconds, 500 Millis)

How it works

The await macro creates an inline function that loops until the timeout expires. At every loop it checks if the condition is true: if that's the case, it returns true. The inline function construct is needed to get a return value, so you can assign it to a variable or embed directly inside an if test. The following code sample gives you an idea of what's happening.

bool wifiConnected = await(WiFi.status() == WL_CONNECTED, 10 Seconds)

// conceptually translates to

bool inline_function() {
    uint32_t start = millis();

    while (millis() - start <= 10000) {
      if (WiFi.status() == WL_CONNECTED)
        return true;

      delay(10);
    }

   return false;
}

bool wifiConnected = inline_function();

L'articolo Eloquent bounded waiting: the await construct proviene da Eloquent Arduino Blog.

]]>
Eloquent non-blocking code: the Every construct https://eloquentarduino.github.io/2019/12/non-blocking-arduino-code/ Thu, 05 Dec 2019 18:42:45 +0000 https://eloquentarduino.github.io/?p=209 The every construct lets you run a piace of code at regular intervals in a fluent way. If you don't need to start, stop, pause your timer, this construct is a valid alternative to more complex timer libraries already available: it only takes a time interval as argument and will execute the code block periodically. […]

L'articolo Eloquent non-blocking code: the Every construct proviene da Eloquent Arduino Blog.

]]>
The every construct lets you run a piace of code at regular intervals in a fluent way. If you don't need to start, stop, pause your timer, this construct is a valid alternative to more complex timer libraries already available: it only takes a time interval as argument and will execute the code block periodically.

Definition

#define every(interval) \
    static uint32_t __every__##interval = millis(); \
    if (millis() - __every__##interval >= interval && (__every__##interval = millis()))

How to use

// these are for greater code readability
#define Millis 
#define Second  *1000
#define Seconds *1000
int interval = 1 Second;

void setup() {
    Serial.begin(115200);
}

void loop() {
    every(1000 Millis) {
        Serial.println("This line is printed every 1 second");
    }

    every(2000 Millis) {
        Serial.println("This line is printed every 2 seconds");
    }

    every(interval) {
        interval += 1 Second;
        Serial.print("You can have variable intervals too! ");
        Serial.print("This line will be printed again in ");
        Serial.print(interval / 1000);
        Serial.println(" seconds");
    }
}

Caveats

every is just a macro definition and is not a proper timer, so it has some limitations:

  1. you can't stop, pause or resume it: once set, it will run forever
  2. its argument must be the suffix of a valid identifier
  3. you can't use several every with the exact same argument: you have to put all the code that needs to happen at the same interval in the same block

Caveat #2

The macro works by generating a variable named like __every__##argument

every(1) ==> uint32_t __every__1;
every(2) ==> uint32_t __every__2;
every(a_given_interval) ==> uint32_t __every__a_given_interval;
every(an invalid interval) ==> uint32_t __every__an invalid interval; // Syntax error
every(1 Second) ==> uint32_t __every__1 *1000; // Syntax error

So every integer literal and any variable are all valid arguments. Any expression is forbidden.

Caveat #3

If you use two every with the exact same argument, two variables with the exact same name will be created and it will rise a compile-time error.

If you can live with this limitations, every only needs the space of an uint32_t to work.

L'articolo Eloquent non-blocking code: the Every construct proviene da Eloquent Arduino Blog.

]]>
How to install the Eloquent library https://eloquentarduino.github.io/2019/12/how-to-install-the-eloquent-library/ Thu, 05 Dec 2019 16:18:48 +0000 https://eloquentarduino.github.io/?p=554 In this short tutorial I'll show you how you can install the Eloquent library to take advange of all the good things it provides to you. It really is super simple, since the Eloquent library is no different from any other library you already installed on your computer, but I'll repeat the steps for clarity. […]

L'articolo How to install the Eloquent library proviene da Eloquent Arduino Blog.

]]>
In this short tutorial I'll show you how you can install the Eloquent library to take advange of all the good things it provides to you.

It really is super simple, since the Eloquent library is no different from any other library you already installed on your computer, but I'll repeat the steps for clarity.

1. Download the zip from Github

The whole library is hosted on Github, so head over to the repo and click Clone or download > Download ZIP.

Download zip from Github repository

2. Extract the zip to you library directory

Once downloaded, you should extract the zip into your Arduino libraries folder. The path will vary based on your OS and installation, but I expect you to know where it is.

3. Strip the -master suffix

When downloaded from Github, your zip and your folder will be named EloquentArduino-master: rename the folder to just EloquentArduino, without the master suffix.

4. Done

If you follwed the steps correctly, the library is ready to be used.


Did you find this tutorial useful? Was is it easy to follow or did I miss something? Let me know in the comments so I can keep improving the blog.

L'articolo How to install the Eloquent library proviene da Eloquent Arduino Blog.

]]>
How to write clean Arduino code: introducing the Eloquent library https://eloquentarduino.github.io/2019/11/how-to-write-clean-arduino-code/ Sun, 03 Nov 2019 16:05:46 +0000 https://eloquentarduino.github.io/?p=164 Eloquent Arduino is an attempt to bring sanity and clarity in Arduino projects. The purpose of this library is to create a wide range of constructs to clearly translate your ideas into meaningful code: stop writing spaghetti code only you can undestand, please! I'll show you how. The problem Arduino sells itself as a platform well […]

L'articolo How to write clean Arduino code: introducing the Eloquent library proviene da Eloquent Arduino Blog.

]]>
Eloquent Arduino is an attempt to bring sanity and clarity in Arduino projects. The purpose of this library is to create a wide range of constructs to clearly translate your ideas into meaningful code: stop writing spaghetti code only you can undestand, please! I'll show you how.

from https://www.wlion.com/blog/5-reasons-you-should-be-writing-clean-code/

The problem

Arduino sells itself as a platform well suited for beginners, and it is for sure; lots of non-tech people are able to bring their ideas to life thanks to this awesome platform.
Nevertheless, I often stumble upon bits of code over the internet that make me question about the quality of the projects people are producing.

Even the Arduino official website is misleading in this sense, in my opinion, since it promotes a code style really straighforward, but suited for toy projects, with little logics and low complexity level.

Here's an example of what I'm talking about, copy-pasted from the Arduino official site (with comments removed):

const int ledPin =  LED_BUILTIN;
int ledState = LOW;
unsigned long previousMillis = 0; 
const long interval = 1000; 

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    digitalWrite(ledPin, ledState);
  }
}

Can you tell what this code does with a minimum mental effort?
I don't think so (you may have recognized the async pattern and it actually blinks a LED in a non-blocking fashion).

THIS is the problem: most Arduino code is not clear at first glance, is not eloquent. By eloquent I mean code that speaks by itself, without the need for comments.


most Arduino code is not clear at first glance, is not eloquent
Click To Tweet


The solution

What about the following?

DigitalOut led(LED_BUILTIN);

void setup() {
    led.begin();
}

void loop() {
    every(1 Second) {
        led.toggle();
    }
}

I swear this is valid code that compiles just fine. Hopefully, it does the exact same thing as above, yet it is far more readable and understandable.

Can you see my point now? Wouldn't it be much easier and reliable to code with the help of a set of such eloquent constructs / interfaces? I strongly believe it is, and this is why I'm writing this library.
Asynchronous programming, pin state managements, animations are bits of code that pop up over and over again in most of the projects, yet every time we start from zero and write the same boilerplate code over and over again.


Boilerplate code is not only tedious, but error-prone. And lengthy. Start writing eloquent code now!
Click To Tweet


Boilerplate code heavily relies on a fixed structure that could be hard to adapt to your specific case. Longer code means more chances to break something and more code to debug, which can waste lots of your valuable time.


I'm starting a series of posts where I'll document some of the classes and constructs the library provides to you. You can find the code on the Github repo.

L'articolo How to write clean Arduino code: introducing the Eloquent library proviene da Eloquent Arduino Blog.

]]>