I've developed a small library that can be used to access Yahoo Weather over NodeMCU and then provide it over the serial port. This can be connected to anything with a serial port - for example, Arduino. In such case, you have an easy way of accessing the weather for your Arduino projects :)
When connecting ESP8266 to Arduino, you need a voltage divider to convert 5v from Arduino to 3.3v.
yahooWeather.lua script provides access to Yahoo Weather. yaw.start() will obtain weather immediately and refresh it every yaw.syncPeriodSec seconds. Weather data itself is stored in yahooWeather.lua -> yaw.weather, you will find their further documentation.
In the example below, you can see how to get wether for Munich and update it periodically:
Weather for today: 01 Sep 2016
18 25 Partly Cloudy
Weather for tomorrow: 02 Sep 2016
16 25 Partly Cloudy
Serial API
The script below registers Serial API Interface providing access to weather. The weather itself will get updated every 17 minutes.
require"serialAPI"require"serialAPIYahooWeather"
yaw.city ="munich"
yaw.country ="de"
yaw.syncPeriodSec =1020-- 17 min
-- setup wlan required by NTP clokc
wlan.setup("fred", "1234567890")
-- start serial API by enabling gpio and uart
sapi.start()
-- start yahoo weather with serial API
yaw.start()
Now you can execute a few simple commands over serial:
# weather description for today
>YF1 text
Rain And Snow
# weather description for tomorrow
>YF2 text
Showers
# not existing command
>YF1 min
ERR:serialAPIYahooWeather.lua:6: attempt to concatenate field '?'(a nil value)# max temp for tomorrow
>YF2 low
1
# weather date for today
>YF1 date
09 Nov 2016
You can find the whole API description in serialAPIYahooWeather.lua
I've been looking for a date and time source for my Arduino projects. Arduino has limited resources, and I would like to use them for the actual work. So I've decided to use NodeMCU with Lua to access date and time from the internet, format it in a way that meets my requirements, and provide such results over the serial port.
There are many ways to access date and time from the internet, but mainly those interfaces change over time, and code using them must be adopted. I was looking for something that would last for years without interaction from my side. That's why I've decided to use Network Time Protocol. There is only one catch: it's UTC time without a date. It was not such a big deal because there are a lot of algorithms out there, so I've just adopted one of them.
Now it's possible to obtain local date and time from NodeMCU over the serial port, and the whole thing is reliable and will last for many years without touching it :)
The implementation is divided into small building blocks; putting them together will give you the desired serial API. This is just a short overview:
Date Format - calculates local date and time based on the timestamp
WiFi Access - simple facade for connecting to WiFi
NTP Time - obtains UTC timestamp form given NTP server
NTP Clock - keeps actual UTC timestamp and synchronizes it periodically with the NTP server
Serial API - finally, this one provides API for date and time
Date Format
Provides functionality to get local date and time from timestamp given in seconds since 1970.01.01
For such code:
collectgarbage()print("heap before",node.heap())require"dateformat"require"dateformatEurope"localts=1463145687df.setEuropeTime(ts,3600)-- function requires GMT offset for your cityprint(string.format("%04u-%02u-%02u %02u:%02u:%02d",df.year,df.month,df.day,df.hour,df.min,df.sec))print("DayOfWeek: ",df.dayOfWeek)collectgarbage()print("heap after",node.heap())
You will get this output:
heap before 44704
2016-05-13 15:21:27
DayOfWeek: 6
heap after 39280
To format the date for the USA, you have to replace "dateformatEurope" with "dateformatAmerica" and call setAmericaTime instead of setEuropeTime. Functionality is divided into small scripts to save some RAM.
WiFi Access
It's a simple facade for connecting to WiFi. You have to provide connection credentials and a function that will be executed after the connection has been established.
execute(...) connects to WiFi, which can take some time. You can still call this method multiple times. In such cases, callbacks will be stored in the queue and executed after establishing a WiFi connection.
Configuring WiFi on: free wlan
status 1
status 1
status 5
Got WiFi connection: 172.20.10.6 255.255.255.240 172.20.10.1
ABC
NTP Time
This simple facade connects to a given NTP server, requests UTC time from it, and once a response has been received, it calls the given callback function.
The example below executes the following chain: WiFi -> NTP -> Date Format.
So in the first step, we create a WLAN connection and register a callback function that will be executed after the connection has been established. This callback function requests time from the NTP server (ntp.requestTime).
On the ntp object, we are registering another function that will get called after the NTP response has been received: printTime(ts).
collectgarbage()print("RAM init",node.heap())require"wlan"require"ntp"require"dateformatEurope";collectgarbage()print("RAM after require",node.heap())ntp=NtpFactory:fromDefaultServer():withDebug()wlan.debug=truelocalfunctionprintTime(ts)collectgarbage()print("RAM before printTime",node.heap())df.setEuropeTime(ts,3600)print("NTP Local Time:",string.format("%04u-%02u-%02u %02u:%02u:%02d",df.year,df.month,df.day,df.hour,df.min,df.sec))print("Summer Time:",df.summerTime)print("Day of Week:",df.dayOfWeek)collectgarbage()print("RAM after printTime",node.heap())endntp:registerResponseCallback(printTime)wlan.setup("free wlan","12345678")wlan.execute(function()ntp:requestTime()end)collectgarbage()print("RAM callbacks",node.heap())
and console output:
RAM init 43328
RAM after require 30920
Configuring WiFi on: free wlan
RAM callbacks 30688
status 1
status 1
status 5
Got WiFi connection: 172.20.10.6 255.255.255.240 172.20.10.1
NTP request: pool.ntp.org
NTP request: 194.29.130.252
NTP response: 11:59:34
RAM before printTime 31120
NTP Local Time: 2016-07-12 13:59:34
Summer Time:
Day of Week: 3
RAM after printTime 30928
NTP Clock
This script provides functionality to run a clock with the precision of one second and to synchronize this clock every few hours with the NTP server.
In the code below, we first configure WiFi access. Once the WiFi access has been established, it will call ntpc.start(). This function will start a clock synchronizing with a given NTP server every minute. Now you can access actual UTC time in seconds over ntpc.current. To show that it's working, we have registered a timer that will call: printTime() every second. This function reads current time as ntpc.current and prints it as local time.
collectgarbage()print("RAM init",node.heap())require"dateformatEurope";require"ntpClock";require"wlan";collectgarbage()print("RAM after require",node.heap())ntpc.debug=truewlan.debug=truewlan.setup("free wlan","12345678")wlan.execute(function()ntpc.start("pool.ntp.org",60)end)localfunctionprintTime()collectgarbage()print("RAM in printTime",node.heap())df.setEuropeTime(ntpc.current,3600)print("Time:",string.format("%04u-%02u-%02u %02u:%02u:%02d",df.year,df.month,df.day,df.hour,df.min,df.sec))print("Summer Time:",df.summerTime)print("Day of Week:",df.dayOfWeek)endtmr.alarm(2,30000,tmr.ALARM_AUTO,printTime)
so this is the output:
RAM init 43784
RAM after require 29408
Configuring WiFi on: free wlan
status 1
status 5
Got WiFi connection: 192.168.2.113 255.255.255.0 192.168.2.1
NTP request: pool.ntp.org
NTP request: 195.50.171.101
NTP response: 17:09:46
RAM in printTime 29664
Time: 2016-08-08 19:10:08
Summer Time: true
Day of Week: 2
RAM in printTime 29808
Time: 2016-08-08 19:10:38
Summer Time: true
Day of Week: 2
NTP request: pool.ntp.org
NTP request: 195.50.171.101
NTP response: 17:10:46
RAM in printTime 29680
Time: 2016-08-08 19:11:08
Summer Time: true
Day of Week: 2
RAM in printTime 29808
Time: 2016-08-08 19:11:38
Summer Time: true
Day of Week: 2
NTP request: pool.ntp.org
NTP request: 131.188.3.221
NTP response: 17:11:46
RAM in printTime 29680
Time: 2016-08-08 19:12:08
Summer Time: true
Day of Week: 2
RAM in printTime 29808
Time: 2016-08-08 19:12:38
Summer Time: true
Day of Week: 2
Serial API
Serial API exposes a simple interface that provides access to diagnostic info and date to be accessed outside NodeMCU - for example, by Arduino.
Serial API is divided into a few Lua scripts. Loading of each script will automatically add new API commands:
- serialAPI.lua - has to be always loaded. It initializes the serial interface with a few diagnostics commands.
- serialAPIClock.lua - access to clock including date formatter.
Each script above registers a set of commands as keys of scmd table and contains further documentation.
The example below provides date access over the serial port:
require"credentials"require"serialAPI"require"serialAPIClock"ntpc.syncPeriodSec=900-- 15 minsapi.baud=115200-- setup wlan required by NTP clockwlan.setup("free wlan","12345678")-- start serial API by enabling gpio and uartsapi.start()-- start NTP synchronizationntpc.start("pool.ntp.org")
Here are few Serial API commands and their responses:
# free ram
>GFR
10664
# WiFi status
>GWS
5
# date and time (24h) in format: yyyy-mm-dd HHLmm:ss
>CF1
2016-09-16 10:45:25
# date in format: yyyy-mm-dd
>CH2
10:45:59
Firmware
Executing multiple scripts can lead to out-of-memory issues on NodeMCU. One possibility to solve it is to build custom firmware containing only a minimal set of node-mcu modules: cjson, file, gpio, net, node, tmr, uart, and WiFi. This blog provides a detailed upgrade procedure: http://maciej-miklas.blogspot.de/2016/08/installing-nodemcu-v15-on-eps8266-esp.html
I've been updating firmware on my NodeMCU a few times without issues - at least up to 0.9
The installation procedure has changed with SDK 1.5 - I've spent a few hours before I got it right, so maybe this post will help you save time.
Here you will find all the required information, but depending on your chip manufacturer, you will need different combinations of some options, and this is exactly the part when it's getting tricky.
Since things are changing rapidly, I will point out exact versions in this tutorial so that it's clear what was working with what at the time of this writing.
How do you tell? I've no idea - just Google "Manufacturer: e0 Device: 4016" and check what people think.
Building custom firmware
Now we have to obtain firmware containing the Lua interpreter for our chip - it's everything you will need to upload Lua scripts over serial. Basically, you will need two binary files:
Obviously, you will need matching versions. There are two options to get those files:
you can download it from my github repo - this will give you SDK 1.5.4.1
you can also use cloud service to build latest release. The link to latest initial-data-block can be found here - just search for esp_init_data_default.bin
I would prefer the latest version - as usual. But in case of some problems, you can try using the version provided on my github repo. At least this one was working in my case with ESP-12E, this might help you to locate a problem.
Erase flash
Put your chip into flash mode as described here. If you have a development board, it should have two buttons: flash and reset. In such case, press flash, and without releasing it, press reset, after that, release flash.
Now you must determine and change the com port in all commands in this tutorial. In my case, it's /dev/tty.wchusbserial1410
Let's finally erase flash!
sudo python esptool.py -p /dev/tty.wchusbserial1410 erase_flash
esptool.py v1.1
Connecting...
Erasing flash (this may take a while)...
Now disconnect ESP from usb port, so that it can fully reset - pressing reset button does not have the same effect!
Flashing new firmware
Put your chip again into flash mode.
I am assuming that you have in your current directory two binary files: one with firmware (nodemcu-dev-7-modules-2016-08-10-10-43-59-integer.bin) and a second with init-stuff (esp_init_data_default.bin).
I wanted to pimp my son's bobby car by installing a few lights, they should go on for a few minutes once he starts moving the car. I've installed a few LEDs and a micro switch attached to the rear axle for this. So that the microswitch will get triggered from time to time.
Connecting LEDs was quite simple, but I had some difficulties building a circuit that would enable the lights for a few minutes. I've decided to use a 555 timer - plenty of examples exist of how to assemble it. I just wanted one that would be energy efficient on stand-by, and the lights should always go off after a few minutes. This was difficult because you have to trigger the 555 by short impulse, otherwise, it will never go off. My solution with micro switch has this one catch: it can remain shorted for a very long time. I could ask my son to keep pushing the car until he hears the second click..... instead, I've used a trigger network.
In the end, I had to find the right values for all components in a way that would guarantee the functionality and ensure low power consumption to not drain batteries over a few days. Actually, I did not calculate those values, I've used an oscilloscope to make sure that timing on 555 pins is right. Probably I've got not a perfect value, but on the other hand side it does what it is supposed to be doing, and it runs on a single battery for months :)
My latest project (https://github.com/maciejmiklas/LEDDisplay) contains the driver for 8x8 LED Modules controlled via
MAX722xx. It allows you to build a display of custom size that is only
limited by the hardware itself. Vertical and horizontal size can contain
up to 256 modules, but before reaching this limit, you would run out of
Slave Select lines for controlling MAX chips, or you would be limited by the amount of RAM. The fact is: you can control a reasonable amount of MAX
chips and build a display of custom size ;)
I've tested the whole idea on display which consist of 8 LED Modules
in horizontal and 3 in vertical position. This gives us 24 modules that
contain 1536 LEDs (88 * 38).
Hardware
First, let's start with the controller, actually, any Arduino will
work, I've used Mega due to the large number of digital output pins. You
could also use Nano with a shift register and alter the way of addressing
Select Slave lines in Display::send(...).
You will need an extra power supply for driving LEDs - assuming that you will use more than one LED Matrix.
Driving single LED Matrix Module
I've used the MAX7219 and 788BS LED Matrix, this is the one with a common
anode. The schematic below illustrates the wiring of LEDs, MAX and Arduino:
This one is equivalent, but instead of single LEDs we have PIN layout of LED Module:
It might happen that your LED Module has a common cathode, in this case, you have to rewire the connection to the MAX chip. You just have to remember that MAX has two sets of relevant pins: Dig0-Dig7
are supposed to be connected to cathodes (-) and SegA-SegG to anodes(+). Such a change will also swap rows with columns within the sine LED
module.
Connecting all LED Matrix together
In the previous chapter, we've seen how to connect a single LED Module with an MAX chip. Now we will connect multiple LED Modules together into one
large display.
Below is the physical display that I've used for testing and examples.
Each LED Module has label indicating its position and Select Slave line.
Each 3-PIN connector on the schematic above symbolizes one module
described in the previous chapter (LED Matrix + MAX72xx), now we've
connected all those modules together.
All MAX722xx chips share common MOSI and SCK lines, MISO is not used, and each chip occupies a separate Slave Select line.
The position of LED Matrix on the schematic above directly
corresponds to their location on the physical display that I've used for
testing. Additionally, each module has a description indicating its
position and Select Slave line, so for example: (2,1) SS: 35 gives us a second module on the third row (counting from zero) and PIN:35 on Arduino for the Select Slave line.
Software
Compiling
We are using standard Arduino libraries, which are already available, the only exception is ArdLog. You have to import this LIB into your IDE. This basically means that you have to download the right release: https://github.com/maciejmiklas/ArdLog/releases/tag/v1.0.0 and unzip it into a folder, where you usually place external libraries. In the case of standard Arduino IDE on Mac, it's ~/Documents/Arduino/libraries. In the end you should have the following structure:
$ pwd
~/Documents/Arduino/libraries/ArdLog
$ ls
ArdLog.cpp ArdLog.h LICENSE README.md
Communication with MAX72xxx
We are using the standard SPI library and Select Slave line on the MAX chip for
addressing. MAX is configured in LED Matrix mode - so there is nothing
special. The setup method can be found in Display::setup()
Setting things up
The main class of our interest will be the Display - it's responsible for the setup of MAX chips and provides API for painting.
Before we start painting, it's necessary to set things up. The code below
creates a 2D array containing Select Slave lines and initializes the display.
The display consists of 3 rows, each with 8 LED Modules.
You can choose any responsible size, but I will stick to this
one.
The layout of the mentioned 2D array corresponds to the physical display:
each LED Module has a dedicated MAX chip, and each chip has a dedicated
Select Slave line. The first dimension of our array indicates the physical row
on display, the second dimension indicates LED Module within this row, and
the value itself contains the PIN number for the Select Slave line.
#include <Display.h>Display*disp;/** * Orientation of LED Kits (8x8 LED matrix) on display that I've used for testing. * The numbers are indicating Select Slave line of MAX7219. * 48, 46, 49, 47, 45, 43, 41, 39 * 36, 34, 32, 30, 28, 26, 24, 22 * 37, 35, 33, 31, 29, 27, 25, 23 */ss_t**ss;ss_t**createSS(){ss_t**ss=alloc2DArray8(3,8);// first rowss[0][0]=48;ss[0][1]=46;ss[0][2]=49;ss[0][3]=47;ss[0][4]=45;ss[0][5]=43;ss[0][6]=41;ss[0][7]=39;// second rowss[1][0]=36;ss[1][1]=34;ss[1][2]=32;ss[1][3]=30;ss[1][4]=28;ss[1][5]=26;ss[1][6]=24;ss[1][7]=22;// third rowss[2][0]=37;ss[2][1]=35;ss[2][2]=33;ss[2][3]=31;ss[2][4]=29;ss[2][5]=27;ss[2][6]=25;ss[2][7]=23;returnss;}voidsetup(){util_setup();log_setup();ss=createSS();// Test display consist of 8x3 LED Modules (3 rows, each one 8 Modules)disp=newDisplay(8,3,ss);disp->setup();}
There is one more method worth mentioning: log_setup(). The whole project has quite a precise logger - so that you can see what is actually
happening. By default, it's disabled, in order to enable it to check out its
documentation: https://github.com/maciejmiklas/ArdLog
Painting on the display
The display consists of a few LED Modules, but from an API perspective, they are
connected together into one continuous canvas. You can place on this
canvas bitmap any position given by such coordinates:
(0,0) -----------------------------> (x)
|
|
|
|
|
|
| (x max, y max)
v (y)
The paint method has the following syntax: paint(pixel_t x, pixel_t y, pixel_t width, pixel_t height, uint8_t data).
It allows you to paint a bitmap on given coordinates with limited width
and height. So you can, for example, paint a bitmap on (3,4) that has
25x3 pixels. It might be larger than the actual display size - in this
case, it will get trimmed.
This is obvious and simple, but there is one catch - you must provide the right data.
This is a 2D array, where the first dimension indicates the vertical and the second
horizontal position on the display. Technically speaking, data is a flat array of pointers, and each pointer points to an array that represents one horizontal line on the display.
Moving over the first dimension of data traverses over lines of the display. The second data dimension represents horizontal pixels within a single line; each byte
represents 8 pixels. Since our display consists of simple LEDs, they can
be either on or off, so each pixel is not represented by
one byte but by one bit. In order to cover 16 pixels in a horizontal
position, we need two bytes, 24 pixels require 3 bytes, and so on.
For example, to fully cover a display consisting of 8x3 LED kits (one used in our examples), we would need data[3][8]. Usually, you will take an array small enough to fit your bitmap and not one that will cover up the whole display.
The paint(...) method updates the internal buffer, in order to send the content of this buffer to MAX chips, you have to call flush().
The idea behind this is to allow you to shift a few bitmaps into the display and then paint the result. You can program a few
independent routines that will update different display parts and
flush all changes at once.
Communication with MAX chips is not very fast, and sending the content of the whole display with every flush() is time-consuming. You can speed up this process by enabling double buffering (set DEOUBLE_BUFFER in Display.h to true). In this case, flush() method will send only bytes that have changed, so you can call flush()
with every loop and not worry about losing performance. The
only drawback is increased usage of RAM: we are creating 2D array that
allocates 8 bytes per each LED Kit plus a few pointers that are usually
required to maintain arrays.
2D arrays in this project have reduced memory footprint because to create a dynamic 2D array, we are creating a single continuous array with a calculated offset (see: alloc2DArray8(....) in DisplayUtil.h).
In this example, we will display a simple static bitmap with 8x8 pixels:
Here is the Arduino sketch: SimpleBitmap, now lest go over it:
First, we have to initialize the display, as we have done in the above chapter Setting things up.
Next, we have to create data that can hold our bitmap - it will have 8x2
bytes. This gives us up to 8 lines and 16 horizontal pixels. But the
size of our bitmap is 9x8 pixels (width x height), which will also be the size of the painted rectangle. It should be as small as possible so you can place another bitmap next to it.
The display will obviously only paint the rectangle given by width/height and not the whole data
array. This is normal that a data array can hold more pixels than the accrual size of our bitmap because size of the data is a multiplication o 8, and the bitmap is not necessary.
voidsetup(){util_setup();log_setup();ss=createSS();disp=newDisplay(8,3,ss);disp->setup();data=alloc2DArray8(8,2);data[0][0]=B01100001;data[0][1]=B10000000;data[1][0]=B01100001;data[1][1]=B10000000;data[2][0]=B01100001;data[2][1]=B10000000;data[3][0]=B01100001;data[3][1]=B10000000;data[4][0]=B01100001;data[4][1]=B10000000;data[5][0]=B00110011;data[5][1]=B00000000;data[6][0]=B00011110;data[6][1]=B00000000;data[7][0]=B00001100;data[7][1]=B00000000;disp->paint(27,9,9,8,data);}voidloop(){util_cycle();log_cycle();// Paint method updates only internal buffer, in order to send data to // MAX chips you have to flush display. disp->flush();delay(100000);}
Static Text
Now we will display static text, actually those are going to be two independent lines.
Here you can find an Arduino sketch containing the whole example: StaticText.
Your sketch needs a setup method, as we've already seen above (chapter: Setting things up), so we will not discuss it again.
In order to display text, you should use StaticText8x8.
The font is defined in Font8x8, each character has 8x8 pixels.
Your code could look like this one (plus initialization stuff from Setting things up):
Display*disp;ScrollingText8x8*message;constchar*textMessage;voidsetup(){util_setup();log_setup();ss=createSS();disp=newDisplay(8,3,ss);disp->setup();message=newScrollingText8x8(disp,48,50,5);message->init();textMessage="This is an example of multiple scorlling areas ;)";message->scroll(8,8,ScrollingText8x8::LOOP,textMessage);}voidloop(){util_cycle();log_cycle();message->cycle();disp->flush();}
The initialization of the display is the same as in the examples above, so it's omitted here.
In order to display scrolling text, we are using ScrollingText8x8. In setup(), we are creating an instance of this class and calling method scroll(...). This part only initializes scrolling but does not play the animation itself. In order to play the animation, you have to call cycle() and flush() in the main loop, and you must not have any additional delays there, otherwise, you might get jagged animation.
During the creation of ScrollingText8x8 we have provided the speed of animation - actually, it's a delay of 50ms per frame. Now calling cycle() in the main loop will produce animation frames according to provided delay. When the time comes, the method cycle() will update the display, and finally, method flush() will send updated content to MAX chips.
The whole implementation of ScrollingText8x8 is nonblocking and consumes CPU only when something is to be done. Internally it's using a simple State Machine.
There is one last thing: you must keep the text used for animation in the global variable to avoid deletion. It's not being copied in the scroll() to avoid memory fragmentation.
Scrolling Text Mixed
This example is similar to the one above, but this time we will display
several scrolling areas:
Here is the sketch.
This code is similar to one with one scrolling area, but this time we have a few:
voidsetup(){util_setup();log_setup();ss=createSS();disp=newDisplay(8,3,ss);disp->setup();uint8_tborderSpeed=20;textUpDown="* * * * * ";up=newScrollingText8x8(disp,64,borderSpeed,1);up->init();up->scroll(0,0,ScrollingText8x8::CONTINOUS_LOOP,textUpDown);down=newScrollingText8x8(disp,64,borderSpeed,2);down->init();down->scroll(0,16,ScrollingText8x8::CONTINOUS_LOOP,textUpDown);textLeftRight="* ";left=newScrollingText8x8(disp,8,borderSpeed,3);left->init();left->scroll(0,8,ScrollingText8x8::CONTINOUS_LOOP,textLeftRight);right=newScrollingText8x8(disp,8,borderSpeed,4);right->init();right->scroll(56,8,ScrollingText8x8::CONTINOUS_LOOP,textLeftRight);message=newScrollingText8x8(disp,48,50,5);message->init();textMessage="This is an example of multiple scrolling areas ;)";message->scroll(8,8,ScrollingText8x8::LOOP,textMessage);}voidloop(){util_cycle();log_cycle();up->cycle();down->cycle();right->cycle();message->cycle();left->cycle();disp->flush();}
We have created a few instances of ScrollingText8x8, each containing different text and positions on the display. In order to play an animation, you have to call cycle() on each instance, but you have to call only once flush(). Each call on cycle() will update its part of the display, and flush will send the changed display to MAX chips.
I am bit tired of using Serial as logger - mainly for two reasons: it does not support sprintf syntax and string are being held in RAM. For this reason I've implemented new library: https://github.com/maciejmiklas/ArdLog
ArdLog serves as simple logger for Arduino that creates formatted messages over Serial:
Each message has timestamp.
Each message within single loop has the same timestamp, so that you can logically connect activities together.
Messages can be formatted using sprintf syntax.
Text for the messages is being held in PROGMEM.
Installation
In order to install ArdLog you have to download desired release and unpack in into folder containing Arduino libraries. The is the result on MacOS:
Logger is disabled by default, in order to enable it set LOG to true.
Messages are created over default Serial port. You can choose alternative port by setting: USE_SERIAL_1, USE_SERIAL_2 or USE_SERIAL_3 to true.
In order to print current time for each message set USE_CURRENT_TIME to true. By default logger will sample time only once at the beginning of each loop.
Getting up and running
Choose suitable configuration in ArdLog.h. In most cases you have to only set LOG to true.
Call log_setup() in setup() method - this will initialize serial port.
Call log_cycle() at the beginning of each loop() - it will sample current time.
Put log messages into #if LOG log(F("....") #endif - once logger is disabled, it will not waste RAM and CUP.