DashFit

For fitness reasons, I have been logging my diet and weight for most of the last 1.5 years. At first, I started with just paper and pen but quickly found it cumbersome to carry a notebook with me whenever I wanted to log.

I quickly moved on to using Google Sheets to keep track of everything. This solved the issue of portability and even cross platform accessibility. However, as I continue to log every single day I have noticed these problems:

  1. Annoying to have to scroll through previous data to get to enter today's entry
  2. Can't keep a history of meals to avoid retyping
  3. Have to manually calculate 7 day average of weight
  4. Mobile app for sheets is buggy (sometimes doesn't load data past the first 1000 rows)
  5. Limited graphing option and graph layout isn't ideal for viewing

Given that logging is now an important part of my life, I have decided to build a web app called DashFit to handle all the fitness related data input/storage/display.

Choosing a development stack

The first order of business is to choose a development stack. At the time of writing, there are more than a dozen popular development stacks.

From previous project experience, I believe it is important to start off with good code architecture to make maintenance and updates easy. Part of this comes from choosing the right stack.

In the UI department, one library that stands out is React. Treating your UI as reusable modules will aid in clarity and long term maintainability. A downside from using React is that it requires building/pre-processing.

I want to keep the build tools as simple as possible, since many of the fancier tools go out of date fairly quickly and have a steep learning curve. My build tool of choice is WebPack, which is simple to use and not hard to learn. The idea behind WebPack is elegant: imagine your dependencies as a tree where each node is a module and edges are requires, you basically provide it the root (react file where you do all the requires) and it traverses the tree until it loads all the dependencies and their respective dependencies.

In terms of the back end, I see no reason to move away from Express and mongoDB. So all in all, I am using the MERN stack (mongo, express, react, node).

Thinking in React

Using React is like learning web development all over again. The thinking in React is very different from any UI work I have done before and one such aspect is styling.

React seems to advocate(they have since taken down the article as of March 2017) inline styling, something that I do not agree with - so I will instead use LESS and style elements like I have always done. However, I also see value in React's way of thinking - which is to keep components their own respective, independent unit including its own styling.

As such, I will use LESS as a preprocessor and organize my file structure such that each component has its own stylesheet in development that gets bundled together for production.

Eg.

|components
  |Header
    |Header.jsx
    |Header.less
  |Nav
  |Container
  |Footer

Notice that the react code also lives in its own folder. WebPack will bundle these as well for production.

Features

  1. User authorization system
    • Use Passport.js to support email login
  2. Weight logging
    • Table view of weight for the week
    • Ability to view and edit week data
    • Undo redo
    • Accept kg. and lb. input
  3. Diet logging
    • Table view of daily caloric intake for the day and week
    • Ability to store custom recipes and their calories/protein
    • Auto complete input based on previous recipes
  4. Data visualization
    • Graph weekly average of weight

Update 1 (10/21/17):

It's been a while but I have been implementing the aforementioned feature throughout the summer. The code can be found on github.

The original feature list now looks like:

  1. User authorization system
    • Use Passport.js to support email login
  2. Weight logging
    • Table view of weight for the week
    • Ability to view and edit week data
    • Undo redo
    • Accept kg. and lb. input
  3. Diet logging
    • Table view of daily caloric intake for the day and week
    • Ability to store custom recipes and their calories/protein
    • Auto complete input based on previous recipes
  4. Data visualization
    • Graph weekly average of weight

So I would say that the core features are implemented but the bells and whistles should still be added on at some point.

Here is what the webpages look like as of now:

This is the first page that I ended up implementing. I wanted the UI to have a modern, lightweight feel so lots of whitespace and simplicity is what I ended up going with.

For styling, I used skeleton as a starting point and LESS as my preprocessor. React is amazingly fast thanks to their virtual DOM diffing process. Lastly, for layout I went for a standard dashboard theme. This design was highly inspired by the dashboard template for Bootstrap but I ended up building the website from grounds up. This is the initial layout I was going with:

You could probably see the similatiry between that and the current design, except I stripped away the color scheme in favor of a flatter tone.

React's modular nature also made it easy to organize components. For each tab on the left (log weight, visualize, log meals), I have defined "widget" that acts as a kind of scaffolding that calls all the components in the page and brings them all to a functional "sub-app".

For this part, I used the react plotting library Recharts. This library had some issues that I ended up actually helping identify but for personal project plotting I think it works well enough but I would be hesistant using it in a production environment.

Pictured above is my weight trend for the last ~2 years. The purple line is the daily weight and the green line is the weekly average.

At this point, I was fed up with the way states were kept. Specifically, they have to "bubble" up to a parent component.

Thankfully, React has a solution for this: Flux! I implemented the meal log using this and I must say: its design philosophy is simply elegant and makes building large UIs much more manageable.

Additionally, it makes code more readable and less likely to fail from improper handling of data.

Right now, I am thinking about refactoring the first two parts of the code using Flux so will likely implement the missing features when I find time for that.

Adafruit ESP 32 Development Board Setup

Instructions are unclear on Adafruit's website regarding how to set this board up, so I decided I would document the steps I took to get HelloWorld working on this development board.

Technical details:
Board: Adafruit Espressif ESP32 Development Board - Developer Edition
OS: OSX Yosemite 10.10.5

Step 1:

Download the Espressif pre built Xtensa Toolchain for OSX by visiting the page and looking for the download link. This page should have the latest tool chain.

Run

mkdir -p ~/esp  
cd ~/esp  
tar -xzf ~/Downloads/xtensa-esp32-elf-osx-1.22.0-61-gab8375a-5.2.0.tar.gz  

Note: If you substituted ~/esp with another directory of your choosing, make sure that there is no space in the path and for the rest of the guide you reference what you used in place of ~/esp. Additionally, your toolchain version may differ from mine so make sure you modify the command appropriately.

Then, to make sure that Terminal can find the binaries, we have to update the PATH variable:

echo '#Adding path variable for ESP32 development' >> ~/.bash_profile  
echo 'export PATH=$PATH:$HOME/esp/xtensa-esp32-elf/bin' >> ~/.bash_profile  
source ~/.bash_profile  

Note: If you used a different directory for ~/esp, replace $HOME/esp with your own.

Now, type in xt and press tab a few times you should see something along the lines of xtensa-esp32-elf-xxx. If nothing happens or you don't see anything like that, then your PATH has not been set up correctly.

Step 2:

cd ~/esp  
git clone --recursive https://github.com/espressif/esp-idf.git  

Note: --recursive is critical, otherwise you have to update the submodules yourself.

Step 2.5:

Pause here. It is important to note that the ESP-IDF build system does not support spaces in esp-idf or project paths. You will end up getting weird errors otherwise.

Step 3:

The Adafruit ESP32 development board comes with a USB<--->UART module; namely, the SILABS CP2102. In order to get this to interface with our Mac, we must install the appropriate driver.

Scroll down and download the one for OSX and click through the installation.

After that, plug in your board via USB, open up Terminal, and type in:

ls /dev/tty.*  

You should see something along the lines of

/dev/tty.SLAB_USBtoUART

Step 4:

We will now get our ESP32 to print hello world to us via serial. First, we grab some example code from esp-idf:

cp -r ~/esp/esp-idf/examples/get-started/hello_world/ ~/esp/ESP32/HelloWorld  
cd ~/esp/ESP32/HelloWorld  

Make sure we set the correct path for ESP-IDF:

export IDF_PATH=~/esp/esp-idf  

Run the terminal interface with the command:

make menuconfig  

Then use your arrow key to go to Serial flasher config -> (xxx) Default serial port then type in the serial port that the ESP32 is connected to from step 3. For me, this is /dev/tty.SLAB_USBtoUART

Exit and save the config.

Run make and you should see the build process.

As an Aside: I got a warning of:

WARNING: Toolchain version is not supported: crosstool-NG crosstool-ng-1.22.0-53-g46f160f-dirty  
Expected to see version: crosstool-NG crosstool-ng-1.22.0-61-gab8375a  
Please check ESP-IDF setup instructions and update the toolchain, or proceed at your own risk.  

which meant that I got the wrong toolchain version because I was following ESP's out of date version of documentation. This has since been fixed and I have updated the guide accordingly. I will just leave this here for reference in case others run into the same issue.

If all went well, you should get a hint at the end of the compile telling you how to flash to the board:

> make flash
Flashing binaries to serial port /dev/tty.SLAB_USBtoUART (app at offset 0x10000)...  
esptool.py v2.0-beta3  
Connecting........__  
Uploading stub...  
Running stub...  
Stub running...  
Configuring flash size...  
Auto-detected Flash size: 4MB  
Flash params set to 0x0220  
Compressed 15872 bytes to 9318...  
Wrote 15872 bytes (9318 compressed) at 0x00001000 in 0.8 seconds (effective 154.3 kbit/s)...  
Hash of data verified.  
Compressed 355216 bytes to 167627...  
Wrote 355216 bytes (167627 compressed) at 0x00010000 in 14.8 seconds (effective 192.7 kbit/s)...  
Hash of data verified.  
Compressed 3072 bytes to 82...  
Wrote 3072 bytes (82 compressed) at 0x00008000 in 0.0 seconds (effective 2150.1 kbit/s)...  
Hash of data verified.

Leaving...  
Hard resetting...  

This means that we have successfully flashed HelloWorld to our board. To check if we really succeeded, run screen /dev/tty.SLAB_USBtoUART 115200 and look at the output. This is what I got:

Restarting in 3 seconds...  
Restarting in 2 seconds...  
Restarting in 1 seconds...  
Restarting in 0 seconds...  
Restarting now.  
E (11211) wifi: esp_wifi_stop 802 wifi is not init  
ets Jun  8 2016 00:22:57  
...
...
...
I (1121) cpu_start: Starting scheduler on PRO CPU.  
Hello world!  
I (201) cpu_start: Starting scheduler on APP CPU.  
This is ESP32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 0, 4MB external flash  
Restarting in 10 seconds...  

Congratulations, you have successfully set up the build environment for the ESP32 and flashed it. You are now well on your way to building the next awesome IOT project!