Rantings from a guy with way too much free time

Program the Esp32 with ESP-IDF on Mac OS X

2019-01-23 programming Rob Baruch

Programming the ESP32 on your Mac!

Go out and buy yourself a cheap little ESP32 SoC and follow these basic instructions to setup your development environment on your Mac. If you want to do some advanced C-programming to control your ESP32, this is for you. If you'd rather use Arduino and play with simple sketches then this is not for you – google how to use Arduino and off you go!

What you will need

So, of course you'll need an ESP32 SoC development board. I recommend the SparkFun Thing. Lots of other choices, but this blog post will address some of the specific configuration considerations that you'll need to be aware of when you select this particular device.

The other items needed are the software development kit (SDK) from Espressif and a toolchain that will compile, build, and link your code. You can get each of these here:

If using screen(1) isn't your cup of tea, another nice alternative is a serial terminal tool available in the Apple Store called Serial. This serial terminal tool is polished and makes your terminal experience pain-free.

So, now that we have all the tools that we need, let's get started. If you're not familiar with virtualenvwrapper, take a look, install it, and let's use it to manage our python environment. The ESP-IDF tools will leverage python to do some of its tasks.

Step 1

Install virtualenvwrapper and create a virtual environment named esp32, and activate it

$ pip install virtualenvwrapper
$ mkvirtualenv -p `which python3` esp32


$ workon esp32
(esp32)  $


Step 2

Install the xtensia gcc toolchain

Before installing the toolchain, let's create a working directory where we'll install everything. For example, in your home directory, create a directory called esp32. Change into that directory and let's go!

Download the toolchain from the link above and untar irt:

tar -xzf ~/Downloads/xtensa-esp32-elf-osx-1.22.0-80-g6c4433a-5.2.0.tar.gz

Next, we need to add the path to the tools to our PATH.

export PATH=$HOME/esp32/xtensa-esp32-elf/bin:$PATH

Next, let's install the ESP-IDF (Software Development Environment). We can install it from the link above. Or if you'd prefer, we can install it from the Github repo:

$ cd ~/esp32
$ git clone --recursive
$ export IDF_PATH=$HOME/esp32/esp-idf

You may want to add both of the exports to your shell's profile so that your terminal will have the environment variables set when you're going to build your projects. Forgetting to set one or both is gonna make for a bad day!

Finally, with the toolchain and the SDK installed, and in our virtualenv, let's install the required python components:

(esp32) $ python -m pip install --user -r $IDF_PATH/requirements.txt

Let's Take it for a Spin!

Okay, we've installed and configured our environment and now we're ready to give it a try! Plug in your ESP32 device into your USB port and let's look at the most basic example. Change into the following directory:

(esp32) $ cd $IDF_PATH/examples/get-started/hello_world

Looking around, we see the following files:

(esp32) $ ls
CMakeLists.txt Makefile      build          main           sdkconfig

Inside the main directory you will find the source file, hello_world_main.c with the following code:

/* Hello World Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"

void app_main()
    printf("Hello world!\n");

    /* Print chip information */
    esp_chip_info_t chip_info;
    printf("This is ESP32 chip with %d CPU cores, WiFi%s%s, ",
            (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
            (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");

    printf("silicon revision %d, ", chip_info.revision);

    printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
            (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");

    for (int i = 10; i >= 0; i--) {
        printf("Restarting in %d seconds...\n", i);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    printf("Restarting now.\n");

A quick look at the code reveals the behavior. We display information about our ESP32 chip, it's revision, and the amount of Flash. Finally we countdown and reboot. The code will do this repeatedly until we reload our device with a new program.

So, how do we get this, or any other program, into our device? We need to follow these three basic steps:

  • make menuconfig – configure the build environment that will set values for our compilation, build, and flash
  • make -j4 all – build the source code in our project directory
  • make flash – install the built executable onto our device

Let's walk through each of these steps one at a time. First we'll configure the build environment using make menuconfig. If you've installed all of the tools correctly, you should see something like this when you execute the command:


If you're using the SparkFun Thing, we need to make the following changes to correctly configure the build:

  • Serial flasher config –> Default Serial Port
  • Component config –> ESP32-specific –> CPU frequency
  • Component config –> ESP32-specific –> Main XTAL frequency

When you plug in your device, look in the /dev directory, and you'll see a device filename like /dev//tty.usbserial-DN028GCB which is the USB-Serial interface to your ESP32 device. Keep in mind, this device name will change when you plug your device into a different USB port. Place that name into the Default Serial Port configuration input.

Next, change the CPU frequency to 240 Mhz for your SparkFun Thing. Lastly, and MOST IMPORTANTLY, set the XTAL frequency to Autodetect. If you fail to do this, the clock divisors will be incorrect and baud rates and wifi clocks will be wrong resulting in code that won't work!!!

After you've made the configuration changes, save your changes in the default sdkconfig (as specified by the filename in the dialog box) and exit the menuconfig back to the shell.

Now that we've configured the build, let er rip! You'll see a ton of compiling, linking, generating, etc…

(eps32) $ make -j4 all
Toolchain path: /Users/robert/src/esp32/xtensa-esp32-elf/bin/xtensa-esp32-elf-gcc
Toolchain version: crosstool-ng-1.22.0-80-g6c4433a
Compiler version: 5.2.0
App "hello-world" version: v3.3-beta1-223-ga62cbfec9-dirty
CC build/app_trace/app_trace.o
App "hello-world" version: v3.3-beta1-223-ga62cbfec9-dirty
CC build/app_update/esp_app_desc.o
CC build/app_trace/app_trace_util.o
CC build/app_update/esp_ota_ops.o
Python requirements from /Users/robert/src/esp32/esp-idf/requirements.txt are satisfied.
CC build/app_trace/host_file_io.o
CXX build/asio/asio/asio/src/asio.o
CC build/app_trace/gcov/gcov_rtio.o
AR build/app_update/libapp_update.a
AR build/aws_iot/libaws_iot.a
AR build/app_trace/libapp_trace.a
CC build/bootloader_support/src/bootloader_clock.o
CC build/bootloader_support/src/bootloader_common.o
CC build/bt/bt.o


Generating libwifi_provisioning.a.sections_info
Generating libxtensa-debug-module.a.sections_info
AR build/wpa_supplicant/libwpa_supplicant.a
Generating libwpa_supplicant.a.sections_info
Generating esp32.common.ld
LD build/hello-world.elf v2.6-beta1
To flash all build output, run 'make flash' or:
python /Users/robert/src/esp32/esp-idf/components/esptool_py/esptool/ --chip esp32 --port /dev/ttyUSB0 --baud 115200 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 /Users/robert/src/esp32/esp-idf/examples/get-started/hello_world/build/bootloader/bootloader.bin 0x10000 /Users/robert/src/esp32/esp-idf/examples/get-started/hello_world/build/hello-world.bin 0x8000 /Users/robert/src/esp32/esp-idf/examples/get-started/hello_world/build/partitions_singleapp.bin

If everything built successfully, you'll see the build products in the project build directory. With your device plugged into your USB port, flash the code you built using the make flash command:

(esp32) $ make flash
Toolchain path: /Users/robert/src/esp32/xtensa-esp32-elf/bin/xtensa-esp32-elf-gcc
Toolchain version: crosstool-ng-1.22.0-80-g6c4433a
Compiler version: 5.2.0
Python requirements from /Users/robert/src/esp32/esp-idf/requirements.txt are satisfied.
App "hello-world" version: v3.3-beta1-223-ga62cbfec9-dirty
Flashing binaries to serial port /dev/tty.usbserial-DN028GCB (app at offset 0x10000)... v2.6-beta1
Serial port /dev/tty.usbserial-DN028GCB
Chip is ESP32D0WDQ6 (revision 0)
Features: WiFi, BT, Dual Core, Coding Scheme None
MAC: 24:0a:c4:00:97:40
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Flash params set to 0x0220
Compressed 24304 bytes to 14537...
Wrote 24304 bytes (14537 compressed) at 0x00001000 in 1.3 seconds (effective 150.0 kbit/s)...
Hash of data verified.
Compressed 145904 bytes to 70051...
Wrote 145904 bytes (70051 compressed) at 0x00010000 in 6.2 seconds (effective 187.0 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 103...
Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.0 seconds (effective 1524.2 kbit/s)...
Hash of data verified.

Hard resetting via RTS pin...

Let's look at our program running. Using our terminal program, connect to our USB device (using the baud rate that we defined in the build, the default rate is 115200). You should output similar to the listing below:

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
mode:DIO, clock div:2
entry 0x40080764
I (31) boot: ESP-IDF v3.3-beta1-223-ga62cbfec9-dirty 2nd stage bootloader
I (31) boot: compile time 23:19:50
I (43) boot: Enabling RNG early entropy source...
I (43) boot: SPI Speed      : 40MHz
I (43) boot: SPI Mode       : DIO
I (46) boot: SPI Flash Size : 4MB
I (50) boot: Partition Table:
I (54) boot: ## Label            Usage          Type ST Offset   Length
I (61) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (68) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (76) boot:  2 factory          factory app      00 00 00010000 00100000
I (83) boot: End of partition table
I (88) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x075d4 ( 30164) map
I (102) esp_image: segment 1: paddr=0x000175fc vaddr=0x3ff80000 size=0x00000 (     0) load
I (105) esp_image: segment 2: paddr=0x00017604 vaddr=0x3ff80000 size=0x00000 (     0) load
I (114) esp_image: segment 3: paddr=0x0001760c vaddr=0x3ffb0000 size=0x01e94 (  7828) load
I (125) esp_image: segment 4: paddr=0x000194a8 vaddr=0x3ffb1e94 size=0x00000 (     0) load
I (132) esp_image: segment 5: paddr=0x000194b0 vaddr=0x40080000 size=0x00400 (  1024) load
I (141) esp_image: segment 6: paddr=0x000198b8 vaddr=0x40080400 size=0x06758 ( 26456) load
I (156) esp_image: segment 7: paddr=0x00020018 vaddr=0x400d0018 size=0x1258c ( 75148) map
I (174) esp_image: segment 8: paddr=0x000325ac vaddr=0x40086b58 size=0x01400 (  5120) load
I (175) esp_image: segment 9: paddr=0x000339b4 vaddr=0x400c0000 size=0x00000 (     0) load
I (180) esp_image: segment 10: paddr=0x000339bc vaddr=0x50000000 size=0x00000 (     0) load
I (189) esp_image: segment 11: paddr=0x000339c4 vaddr=0x50000000 size=0x00000 (     0) load
I (200) boot: Loaded app from partition at offset 0x10000
I (204) boot: Disabling RNG early entropy source...
I (210) cpu_start: Pro cpu up.
I (214) cpu_start: Application information:
I (219) cpu_start: Project name:     hello-world
I (224) cpu_start: App version:      v3.3-beta1-223-ga62cbfec9-dirty
I (231) cpu_start: Compile time:     23:19:53
I (236) cpu_start: Compile date:     Jan 23 2019
I (241) cpu_start: ESP-IDF:          v3.3-beta1-223-ga62cbfec9-dirty
I (248) cpu_start: Starting app cpu, entry point is 0x40080e00
I (227) cpu_start: App cpu up.
I (259) heap_init: Initializing. RAM available for dynamic allocation:
I (266) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (272) heap_init: At 3FFB2EE0 len 0002D120 (180 KiB): DRAM
I (278) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (284) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (291) heap_init: At 40087F58 len 000180A8 (96 KiB): IRAM
I (297) cpu_start: Pro cpu start user code
I (17) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
Hello world!
This is ESP32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 0, 4MB external flash
Restarting in 10 seconds...

That's it! You've successfully installed, built, and flashed your C-program to your ESP32 device! One last tip. Be sure to disconnect your serial terminal program from the device when you want to reflash. Failing to do so will cause your flashing to fail as the device will be busy.

Next Steps

Now that you know how to build projects, go take a look at a more advanced project found in the ESP-IDF examples directory. Be sure to follow the three steps above. In more complicated examples, you may need to configure project-specific items (such as SSID and passwords for wifi). Read the file for project-specific details.

Finally, all that you want to know about the API can be found here.

Go have fun!!

Julia Intro

2018-11-07 programming
Juliaet, Wherefore art thou? As Yoda once said… > YAPL - yet another programming language. Learn something new, you must. Why Learn Julia? Julia was designed from the beginning for high performance. Julia programs compile to efficient native code for multiple platforms via LLVM. As such, it's an interesting programming language to take a look at as it's a serious contender for certain classes of programming problems; most notibly, scientific and data-centric analysis. Continue reading

OCaml OpenGL - Get into Gear!

2018-03-13 programming Rob Baruch
OCaml and OpenGL - Getting our Functional Programming into Gear! In my blog post for this day, I thought I'd take a look at the OCaml OpenGL library, lablgl. If you're not already familair with openGL, I strongly suggest that you take a look at one of the tutorials available online. One that I found to be very informative; although written in c++, is opengl-tutorial. Nonetheless, in this post, we'll look at some simple, and not so simple examples written in OCaml. Continue reading

Let's Get Funky with the Camel

2018-02-09 programming Rob Baruch
How many ways can we get Fun(ky) In any programming paradigm, it's critical to understand how we write functions - be they traditional imperative , anonymous , recursive, or functional. In this post, I will break down the different types of functions that you can write in OCaml. Let's start by examining the imperative function. Here's a simple function that prints out the phrase Hello World! $n$ times, once on each line, and returns the value $n$ as its result. Continue reading

OCaml Hello World and more

2018-02-06 programming Rob Baruch

Well Hello There!

As tradition has it, every new programming language experience must begin with the basic Hello World. Let's walk the the basics of how to set up our development environment specifically for OCaml and demonstrate how to compile our basic program.

Warming up our Environment

Of course we could just use our vanilla editor to enter our OCaml programs, but a more efficient work environment leverages an extensible editor that is aware of our programming language. Editors like sublime, Atom, and Visual Studio Code are some of the more popular selections. Vim and Emacs are also options, but the new tools tend to support vi/emacs style editing while offering more extensive and expansive features.

In this series, I will use Visual Studio Code. And here are some tools that you might want to add to make your experience more enjoyable when writing OCaml code.

OCaml for VS Code

Install the OCaml and Reason IDE extension and reload after the installation completes. This extension will:

  • highlighting
  • editing
  • navigation
  • static analysis

Before you install this extension be certain to install the opam package merlin. Once installed, you have a convenient IDE setup for writing OCaml with VS Code. Cool, now with the editor installed and configured, let's fire it up and save our first OCaml file,

let main ()  =
        output_string stdout "Hello world!\n";;

main ()  ;;

We can compile our simple Hello World! program using the OCaml compiler, ocamlc. To compile:

$ ocamlc -c
$ ocamlc -o hello hello.cmo

$ ./hello
Hello world!

Ta Da! That's it! So, *Hello World isn't so exciting, let's move beyond this trivial example to some more insightful demonstrations of language features in OCaml.

Basics Beyond Hello World!

Now that we're all setup to write and compile OCaml programs, let's enumerate through some basic stuff to expand your knowledge of the language features available to us programmers. If you're already a polyglot, a great source of “How to x in language y” is available at RosettaCode. Take a look for example at how we might solve the Sirpinski Carpet. We can take a look at a traditional solution in Golang. And then, understanding the algorithmic implementation, we can see how we might solve the same probmelm in OCaml.

Here's the Golang implementation:

package main
import (
var order = 3
var grain = "#"
func main() {
    carpet := []string{grain}
    for ; order > 0; order-- {
        // repeat expression allows for multiple character
        // grain and for multi-byte UTF-8 characters.
        hole := strings.Repeat(" ", utf8.RuneCountInString(carpet[0]))
        middle := make([]string, len(carpet))
        for i, s := range carpet {
            middle[i] = s + hole + s
            carpet[i] = strings.Repeat(s, 3)
        carpet = append(append(carpet, middle...), carpet...)
    for _, r := range carpet {

And now here's a crack at how you'd write the solution in OCaml

let rec in_carpet x y =
  if x = 0 || y = 0 then true
  else if x mod 3 = 1 && y mod 3 = 1 then false
  else in_carpet (x / 3) (y / 3)
(* I define my own operator for integer exponentiation *)
let rec (^:) a b =
  if b = 0 then 1
  else if b mod 2 = 0 then let x = a ^: (b / 2) in x * x
  else a * (a ^: (b - 1))
let carpet n =
  for i = 0 to (3 ^: n) - 1 do
    for j = 0 to (3 ^: n) - 1 do
      print_char (if in_carpet i j then '*' else ' ')
    print_newline ()

  carpet 3;;

The first thing to notice is that both implementations focus on using its respective language features to solve the problem. Golang, not being an inherently recursive language implements the (naturally recursive problem statement) iteratively. OCaml, with strong functional orientation, solves the problem using recursion. Moreover, notice the reserved keyword, rec, preceeding let. This keyword explicitly tells OCaml that we are defining a recursive function. More on that later.

What's in the (OCaml) Box - A survey of language features.

For those that prefer examining specific language elements one at a time rather than infer meaning from other programs, Below is a review of what elements comprise the basics of OCaml.

Basic Assignment

Simple assignments. Here we can see assignments can be singular, or multi-valued. Note the response from assignment. The interpreter responds with the val type of the expression. OCaml is a strongly-typed language. As such, the interpreter will tell you what the type is based on the value of the expression.

# let a = 1 ;;
val a : int = 1

# let a,b = 1,2.;;
val a : int = 1
val b : float = 2.

Beyond simple data-types of ints, floats, and strings, we can have more complex types like Lists. Here we assign l to the list expression containing strings. Lists are homogeneous - they must contain all of the same type, be that primitive or user-defined.

# let l  = [ "a"; "list"; "of"; "words"]
val l : string list = ["a"; "list"; "of"; "words"]

For example, we can define a type, dog that is a record of an int representing age, and string representing name.

# type dog = { age : int; name : string; }
type dog = { age : int; name : string; }

# let pound = []
  let pound = {age = 2; name = "fluffy"} :: pound
  let pound = {age=3; name = "penny"} :: pound
  let pound = {age=5; name="max"} :: pound

val pound : dog list =
  [{age = 5; name = "max"}; {age = 3; name = "penny"};
   {age = 2; name = "fluffy"}]

Another point of caution. We delcared an empty list and then used the list-concatenation operator to add elements to our list. If we inspect the length of our list, List.length pound, we see it's of length $3$. Notice what happens if we construct a list as follows:

# let pound = [{age = 2; name = "fluffy"}, {age=3; name = "penny"}, {age=5; name="max"}];;

val pound : (dog * dog * dog) list =
  [({age = 2; name = "fluffy"}, {age = 3; name = "penny"},
    {age = 5; name = "max"})]

Notice the different types each assignment creates. The first one creates a dog list typed collection, while the second one creates a three-tuple-dog list containing one element. The short story is that you need to be careful when creating lists verses tuples. Here's another way to use the :: operator to do multiple concatenation all in one assignment.

let pound = []
let pound = {age = 2; name = "fluffy"} :: {age=3; name = "penny"}  ::{age=5; name="max"} :: pound;;

More about strict types

So clearly OCaml is going to play rough with types. This behavior is no less stringent with integers and floats in numerical expressions. Take a simple function definition to sum two numbers (more on function definitions later).

# let sum a b = a + b;;
val sum : int -> int -> int = <fun>

Notice here the value of sum: it's explicitly typing our function to add solely integers. If we tried to add an integer and a float, look what happens:

 # sum 2  3. ;;
 Error: This expression has type float but an expression was expected of type

Fortunately, type-casting is available to explicitly align our types:

# sum 2 (int_of_float 3.) ;;
- : int = 5

Going further, every expression is strongly typed. Watch what happens when we define a new function using our sum function.

# let sum' = sum 5 ;;
val sum' : int -> int = <fun>

Here we define sum’ (notice the ' is a valid character in a variable name) as the sum function and a $5$. And it tells us that this is a new function!! We didn't use the function keyword, so how is this a function? Well, OCaml knows that this is a function and the second argument to sum is an int but it's missing. So sum’ is a function that now takes only one int as the $5$ is the first argument to sum in the expression. Watch what happens when we call our new function.

# sum' 6;;
- : int = 11

# let s = sum' 5 + sum 3 4 ;;
val s : int = 17

Functional or Imperative – Your Choice!

Before we finish this post, let's look at some imperative programming in OCaml. Let's use the built-in Array type and create an array of integers. This is our traditional mathematical vector object. We can create a function that computes the point-wise product of two vectors easily using loops:

let (^*) a b =
let len = min (Array.length a) (Array.length b) in
let prod = Array.make len 0.0 in
for i = 0 to len - 1 do
    prod.(i) <- a.(i) *. b.(i)

So, at first glance, this looks super-confusing. But breaking it down will quickly show that it's actually pretty easy to read! We create a function that takes two parameters, a, and b. And the name of the two-parameter function is ^. Wait, what?? We can do that? Yup. By wrapping the two characters with the parentheses, we name our new function as such. Moreover, we can use it as an infix function as it's now a newly defined operator. Next we see we use the built-in Array object to get the length of the two parameters and construct a new array, prod that is of size as big as the smallest vector. We didn't type our parameters, so how does it know that we are dealing with arrays? Since we use it as a parameter to Array.length our function operator infers that we mean for the parameters to be of type Array. And in fact more than just type Array, but Array float as wee use the float multiply operator rather than the int multiply operator (** *. ** verses ** * **, respectively).

And now we use the imperative for-loop construct to loop over the vector elements, multiply them together (using the float-multiply operator) and assign them to our prod. We return the value of the prod as the expression of our function.

# let a = Array.make 5 2;;
  let b = Array.make 5 2.;;

  let c = a ^* b;;
val c : float array = [|4.; 4.; 4.; 4.; 4.|]

Next up, more on functions…

Recursion Revisited

Recursion, from more than one point-of-view.

A common programming idiom in computer science is solving a problem by self-reference, also known as recursion. In this post, we look at two different implementations of the same problem.

Solve a recursive problem in two different programming language paradigms

Let's look at the solution to a simple problem, compute $f(x)=e^{x}$.

We will illustrate two separate solutions - one in a procedural language (python), and the other in a functional language (elixir).

Let's start off with the functional language. Were does recursion come into play?

We define the function $f(x)=e^x$ as the infinite sum, $\text{ }f(x) = \sum_{n=0}^\infty{\frac{x^n}{n!}}$

In our solution below, we define two separate recursive functions, exp/2 and fac/1. What's interesting to note here is how each of these functions has two separate definitions. This is an aspect of programming in elixir that elegantly uses pattern-matching to return different results depending upon the input. Our two functions nicely dovetail into the base-case and recursive case of a recursive algorithm.

For example, looking at exp/2, the first function definition returns 1 for any value of x (as indicated by the _ preceding the variable) and returns 1. This is the mathematical equivalent of $x^0=1\text{ for any }x$.

The second definition of exp/2 is the recursive case. for any value of $n\gt0$. Moreover, we define exp(x, n) as $\frac{e^x}{n!}$ + exp(x, n-1).

Similarly for the definition of fac/1 we see two definitions; one for $n=0$ and another for all values of $n\gt0$.

Continue reading

Visualizing the News: Grab your PILlow

2017-11-12 python programming
A Picture's Worth a Gazillion Bits newsapi This weekend, I tripped over a neat news REST-ful api called newsapi. Grab an API key and you're off to the races. There are tons of live headlines - News API can provide headlines from 70 worldwide sources. There are basically two api endpoints: GET GET Register for an account and generate an api-key and let's get started. Accessing the API with Python newsapi can easily be accessed using a browser since the REST-ful method used is a GET method. Continue reading
Older posts