Setup NodeJS Project Space on Raspberry Pi Zero W

Setup the Arch Linux SD Card

This article will be building off a previous article, where I walked through a headless setup of Arch Linux on the Raspberry Pi Zero W (rp0w). And if you aren’t familiar with the term “headless setup,” essentially, we are talking about setting up the SD card so you don’t have to plug it into a monitor. You can plug it in to your rp0w, boot it, and SSH in.

Now you’ve setup the Arch Linux card and SSH’ed lets go through setting up a NodeJS environment on the rp0w. Luckily, there have been people smarter than me who’ve already done some heavy lifting for us.

Alright, start by SSH’ing into your Raspberry Pi.

Running a NodeJS Install Script

Now we are at the Raspberry Pi command prompt we are going to run a script which will pull down the latest version of NodeJS built for ARM and install it to the Raspberry Pi.

But before we can do that we need to install a few helper programs

At the command prompt type and say “yes” when prompted.

sudo pacman -S wget

Wget is a package which allows direct download of Internet content from the command prompt.

Now, we will run a command which pulls a NodeJS installation script of the Internet and run it. This script was written by audstanley and can be found at

If you like the script, you should go buy audstanley a coffee – the link to do so is the Github page.

As of this writing, the script downloads the latest version of NodeJS for your architecture (that’s the tricky part), installs it, then creates the appropriate symbolic links for NodeJS and npm to work correctly.

Ok, enough preamble.

To install NodeJS type

sudo wget -O - | sudo bash
node -v

That’s it!

We can now create a new node project by typing

mkdir my_node_project
cd my_node_project
npm init

If you want to learn more about NodeJS, I recommend the Udemy course:

I’m not getting kick backs; it’s the course I used to get and liked it. I actually used the Raspberry Pi for the code he walks through building and didn’t have a problem.

Lag() before MySQL 10.2

Recently I was required to work with MySQL 10.0. I was surprised to find MySQL 10.2 and less does not support some common Windowing Functions, specifically, Value Functions and Rank Functions.

Well, bummer. I really needed them.

On top of it, I only had read access to the database without the ability to create a stored procedure. Out of desperation, I found myself researching the possibility of creating my own functions using MySQL User Variables.

Slightly tweaking Dante, “Abandon all normal, ye who enter here.” User variables are weird.

I’ve spent some time researching on the interweb and about the best article I’ve found on the subject is:

Advanced MySQL User Variable Techniques

Which focuses on getting the desired behavior from user variables rather than understanding them. This article is going to stick with the same philosophy–I don’t need to necessarily understand them, however, I want to be able to predict their behavior.

One word of warning, the above article is not really a suggessted reading before this article–it’s more of required reading. If you don’t know how to force the user variables to be evaluated when you need them, then the results are unpredictable.

The TL;DR version: Order of operations matter a lot in user variables and wrap the user variable in a subquery or function to force evaluation.

At this bottom of the article I’ve included the data used in this article. You can insert it into a MySQL or MariaDB database and follow along. The goal is to convert these data into a start_date and stop_date which would greatly reduce the storage needs.

For id 1 the start_date and stop_date equivalents would look like:

id date  
1 09/10/12 start_1
1 09/11/12  
1 09/12/12  
1 09/13/12  
1 09/14/12 stop_1
1 10/11/12 start_2
1 10/12/12  
1 10/13/12 stop_2

We want to end up with a table like below.

id start_date end_date
1 09/10/12 09/14/2012
1 10/11/22 10/13/2012

To transform the data into this table it’s important to know user variables can hold a value from one row to the next.

    id, date, @row_number:=@row_number + 1 row_num
        CROSS JOIN
    (SELECT @row_number:=0) r;

This should produce the following table:

id date row_num
1 2012-09-10 1
1 2012-09-10 2
1 2012-09-11 3
5 2013-02-07 4
5 2013-02-07 5
5 2013-02-07 6
5 2013-02-07 7
5 2013-02-07 8
5 2013-02-07 9

Pretty cool, right? Now, if only we could get the row_number to reset whenever the id changes. No worries, let’s use another variable to store the id from the previous row so we can compare it to the current.

    @row_number:=@row_number + 1 row_number,
    IF(@previous_id = id,
        @row_number:=0) calc1,
    @previous_id:=id cacl2
        CROSS JOIN
    (SELECT @row_number:=0, @previous_id:=0) r;

This should give us the following output:

id date row_number calc1 cacl2
1 2012-09-10 1 0 1
1 2012-09-10 1 1 1
1 2012-09-11 2 2 1
5 2013-02-07 3 0 5
5 2013-02-07 1 1 5

Notice, the calc1 and calc2 are not values you need. They are merely calculations used to reset the row_number whenever the id changes. Hmm, this is interesting–and, hopefully, you can see it has many possibilities.

Now, let’s go back and think about our problem a bit more.

id date
1 09/10/12
1 09/11/12
1 09/12/12
1 09/13/12
1 09/14/12
1 10/11/12
1 10/12/12
1 10/13/12

We can save a value from one row to the next. Therefore, detecting the breaks in a range of attendance dates can be obtained by comparing the current row’s date value to the previous row’s date value. If the previous row is greater than the current row minus one, then we know there was a break in the range.

    id, date, range_selector
            IF(@previous_id = id, @range_selector, @range_selector:=0) calc1,
            IF(DATEDIFF(@previous_date, date) = 1, @range_selector, @range_selector:=@range_selector + 1) range_selector,
            @previous_id:=id calc2,
            @previous_date:=DATE(date) calc3
    ORDER BY id DESC , date DESC) order_sub
    ) variable_initialization
    ORDER BY id , date DESC) date_ranges;

This should give the following table:

id date range_index
1 2012-10-13 1
1 2012-10-12 1
1 2012-10-11 1
1 2012-09-14 2
1 2012-09-13 2
1 2012-09-12 2
1 2012-09-11 2
1 2012-09-10 2
2 2012-09-23 1
2 2012-08-22 2
2 2012-08-17 3
2 2012-08-12 4
2 2012-08-11 4
2 2012-08-10 4
2 2012-08-09 4
4 2012-11-22 1
4 2012-11-03 2
4 2012-11-02 2
4 2012-11-01 2
4 2012-10-04 3
4 2012-10-03 3
4 2012-10-02 3
4 2012-10-01 3
5 2013-02-07 1
5 2013-02-06 1
5 2013-02-05 1
5 2013-02-04 1
5 2013-02-03 1
5 2013-02-02 1
5 2013-01-28 2
5 2013-01-24 3
5 2013-01-23 3
5 2012-12-07 4
5 2012-12-06 4
5 2012-12-05 4
5 2012-12-04 4
5 2012-12-03 4
5 2012-12-02 4
5 2012-12-01 4
5 2012-11-01 5

The reason I state “should”, if you modify the order of the user variables, it’ll break. If you change the ORDER BY, it’ll break. If you add a WHERE or HAVING clause, it’ll break. Pretty much, it’s as fragile a query as they come.

However, the clever bunch probably see where we are going with this. Now, it’s simply a matter of taking the MIN() and MAX() of of date and group by the id and range_index.

    id, min(date) start_date, max(date) end_date
            IF(@previous_id = id, @range_selector, @range_selector:=0) calc1,
            IF(DATEDIFF(@previous_date, date) = 1, @range_selector, @range_selector:=@range_selector + 1) range_selector,
            @previous_id:=id calc2,
            @previous_date:=DATE(date) calc3
    ORDER BY id DESC , date DESC) order_sub
    ) r
    ORDER BY id , date DESC) date_ranges
    GROUP BY id, range_selector;

Which should provide us with output like:

id start_date end_date
1 2012-10-11 2012-10-13
1 2012-09-10 2012-09-14
2 2012-09-23 2012-09-23
2 2012-08-22 2012-08-22
2 2012-08-17 2012-08-17
2 2012-08-09 2012-08-12
4 2012-11-22 2012-11-22
4 2012-11-01 2012-11-03
4 2012-10-01 2012-10-04
5 2013-02-02 2013-02-07
5 2013-01-28 2013-01-28
5 2013-01-23 2013-01-24
5 2012-12-01 2012-12-07
5 2012-11-01 2012-11-01

And there we go. Not too amazing, but I couldn’t find this answer by Googling, so I figure I’d add it to the great Wiki in the Sky.

CREATE TABLE attendance(
  ,date DATE  NOT NULL
INSERT INTO attendance(id,date) VALUES (1,'2012-09-10');
INSERT INTO attendance(id,date) VALUES (1,'2012-09-11');
INSERT INTO attendance(id,date) VALUES (1,'2012-09-12');
INSERT INTO attendance(id,date) VALUES (1,'2012-09-13');
INSERT INTO attendance(id,date) VALUES (1,'2012-09-14');
INSERT INTO attendance(id,date) VALUES (1,'2012-10-11');
INSERT INTO attendance(id,date) VALUES (1,'2012-10-12');
INSERT INTO attendance(id,date) VALUES (1,'2012-10-13');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-09');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-10');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-11');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-12');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-17');
INSERT INTO attendance(id,date) VALUES (2,'2012-08-22');
INSERT INTO attendance(id,date) VALUES (2,'2012-09-23');
INSERT INTO attendance(id,date) VALUES (4,'2012-10-01');
INSERT INTO attendance(id,date) VALUES (4,'2012-10-02');
INSERT INTO attendance(id,date) VALUES (4,'2012-10-03');
INSERT INTO attendance(id,date) VALUES (4,'2012-10-04');
INSERT INTO attendance(id,date) VALUES (4,'2012-11-01');
INSERT INTO attendance(id,date) VALUES (4,'2012-11-02');
INSERT INTO attendance(id,date) VALUES (4,'2012-11-03');
INSERT INTO attendance(id,date) VALUES (4,'2012-11-22');
INSERT INTO attendance(id,date) VALUES (5,'2012-11-01');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-01');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-02');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-03');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-04');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-05');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-06');
INSERT INTO attendance(id,date) VALUES (5,'2012-12-07');
INSERT INTO attendance(id,date) VALUES (5,'2013-01-23');
INSERT INTO attendance(id,date) VALUES (5,'2013-01-24');
INSERT INTO attendance(id,date) VALUES (5,'2013-01-28');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-02');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-03');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-04');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-05');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-06');
INSERT INTO attendance(id,date) VALUES (5,'2013-02-07');
Setup i2c on Raspberry Pi Zero W using Arch Linux

This article builds on the previous, where I ran us through setting up Arch Linux for the Raspberry Pi Zero W.

Let’s not stop, let’s get I2C going so we can interact with some cool hardware.

1. Installing sudo

If you’ve followed my previous guide on installing Arch Linux on a Raspberry Pi then you’ll have ended up with a bare bones system, which is good. No unneeded fat. But sometimes fat is needed, it’s what gives us curves, and curves are beautiful….I feel this metaphor is breaking down. In short, we need extra packages to get work done.

The first package we need is sudo

It will allow us to easily manage file permissions.

First, make sure your system is up to date. To do this we are going to login as the root user. You can do this by typing su followed by the root user’s password, which for a barebone Arch Linux installation is root.

$ su
Password: root

Oh, and if you haven’t figured out yet, alarm stands for Arch Linux ARM.

Next we need to update the package libraries and system.

pacman -Syu

After hitting enter, it should look like this:

root@alarmpi alarm]# pacman -Syu
:: Synchronizing package databases...
 core                                                        179.0 KiB   448K/s 00:00 [#################################################] 100%
 extra                                                      1982.8 KiB  1279K/s 00:02 [#################################################] 100%
 community                                                     4.0 MiB  1689K/s 00:02 [#################################################] 100%
 alarm                                                        35.0 KiB   583K/s 00:00 [#################################################] 100%
 aur                                                           6.0 KiB  0.00B/s 00:00 [#################################################] 100%
:: Starting full system upgrade...
resolving dependencies...
looking for conflicting packages...

It should give you a list of packages with update and upgrade candidates and prompting you to confirm the updates. Go ahead and say yes.

Now we should be good to install sudo

$ pacman -S sudo

Even after sudo is installed, we still need to add the main user, which is alarm to the sudo’er group. This in effect gives the alarm user the superpowers of the root user.

Now, the way sudo works is by adding a user to a special Linux group. Anyone added to this group will be given root superpowers. To get a list of those currently in the sudo group:

sudo -ll

You should get something like

User root may run the following commands on alarmpi:

Sudoers entry:
    RunAsUsers: ALL

Ok, let’s get the alarm user added to the sudoer group.


EDITOR=nano visudo

This should allow you to edit the visudo file and add alarmpi to sudoers. Oh, the write permissions for the visudo file are limited to root, so if you have switched back from the root user to alarmpi you will need to run su again and log back in as root before editing this file.

Let’s find the entry for adding users to the sudo’er group.

Find the part which looks like this:

## User privilege specification
root ALL=(ALL) ALL

And add alarm ALL=(ALL) ALL right below the root entry. It should look like this after editing.

## User privilege specification
root ALL=(ALL) ALL
alarm ALL=(ALL) ALL

Then hit CTRL+O to write the changes and CTRL+X to exit.

Before we can check the changes took, we will need to exit our root session.


This should land you back at your alarm session. To see you the alarm user is now added to the sudoer group type

sudo -ll

And if all went well, you’ll get this output

User alarm may run the following commands on alarmpi:

Sudoers entry:
    RunAsUsers: ALL

Notice, we now have access to ALL commands. Booyah!

We can do a hard test by typing:

sudo ls

We should get

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

[sudo] password for alarm:

Type the alarm user password (which is alarm, if you haven’t changed it).

2. Install needed packages

pacman -S git python2 i2c-tools base-devel python2-distribute python2-pip

Use Python’s Package Index (pip) to install Raspberry Pi GPIO support

sudo pip2 install RPi.GPIO

3. Install raspi-config

sudo pacman -S xorg-xrandr libnewt
git clone
cd raspi-config
makepkg -i

Use the Raspi-Config tool to enable I2C

sudo raspi-config

Select “Interfacing Options” and enable I2C.

Note: Going back through these instructions I did notice when I started raspi-config I received this warning:

/usr/bin/raspi-config: line 997: warning: command substitution: ignored null byte in input

And when I attempted to enable I2C it gave this error.

* Failed to read active DTB

But it still seemed to do the job. I’ll investigate more when I’ve time.

Now, we need to reboot the system for everything to register.

4. Test the I2C Setup

We should be all setup. Try running

sudo i2cdetect -y 1

If all has went well then you should get

[alarm@alarmpi ~]$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Now, we just need to connect an I2C device to the bus and we should the hex address of where the device may be found.

Installing Arch Linux on Raspberry Pi with Immediate WiFi Access

Disclaimer: The easiest way to setup an SD card with Arch Linux for the Raspberry Pi Zero W (rp0w) is using Linux–and the following guide will assume you have access to Linux somewhere. For Mac and Windows users, it is possible to setup an SD card using Linux inside of a virtual machine. The interwebs will have more on the subject.

The hardest part of setting up Arch Linux for the rp0w is getting the WiFi working on boot. This allows accessing the OS through ssh immediately. This is known as a “headless setup.” I’ve created instructions on doing something similar in Kali. However, I was lucky when I hit Arch–as there is a fine fellow who has already written a script to setup the WPA Supplicant needed for a headless build.

1. Create an SD Card by following the Arch Linux instructions

Really, the only piece of information not provided by Arch Linux community is which ARM architecture you need for the rp0w. It’s armv6.

A few notes on using the installation instructions.

  • I had to run most of the commands as root (sudo)
  • We are going to insert a step afte the SD card is setup and before we boot our rp0w
  • MOST IMPORTANT NOTE: If you accidently select a different device instead of your SD card bad poop will happen. For real. To know which device is your card make heavy use of fdisk -l which will provide a list of all devices. Your SD card is approximately the same size as the card states. For example, this is the output I get when I run fdisk -l on my PC with the SD card in.
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt

Device         Start       End   Sectors   Size Type
/dev/sda1         40    409639    409600   200M EFI S
/dev/sda2     409640 578929663 578520024 275.9G unkno
/dev/sda3  578929664 586480023   7550360   3.6G Micro
/dev/sda4  586480024 586742167    262144   128M Apple
/dev/sda5  586743808 976842751 390098944   186G Linux
/dev/sda6  976842880 977105023    262144   128M Apple

Cleaning up

Disk /dev/sdb: 7.5 GiB, 8053063680 bytes, 15728640 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xd0ca12f8

Device     Boot  Start      End  Sectors  Size Id Type
/dev/sdb1         2048   206847   204800  100M  c W95
/dev/sdb2       206848 15728639 15521792  7.4G 83 Linu

So, the main device path for my SD card is /dev/sdb. And to the first partition it’s /dev/sdb1

2. Create Script to Enable WiFi on Boot

We are going to need to create a script on the Linux OS you used to setup the SD card. This script will access the rp0w’s Arch Linux files and inject our WiFi information. This will allow the rp0w to automatically connect to your WiFi router when it boots, thus, giving you immediate access to it via SSH.

At the command prompt (of your PC, not the rp0w)


This will open a blank nano editor. Inside, paste the following, then save the file.


set -e

if [[ $# -ne 3 ]] ; then
   echo "Usage: $0 </dev/disk> <ssid> <passphase>"
   exit 1


if [[ ! -b "${DISK}" ]] ; then
   echo "Not a block device: ${DISK}"
   exit 1

if [[ "${USER}" != "root" ]] ; then
   echo "Must run as root."
   exit 1

echo Mounting
mkdir root
mount "${DISK}2" root

cat << EOF >> root/etc/systemd/network/


wpa_passphrase "${SSID}" "${PASS}" > root/etc/wpa_supplicant/wpa_supplicant-wlan0.conf

ln -s \
   /usr/lib/systemd/system/wpa_supplicant@.service \

echo Unmounting
umount root

echo Cleaning up
rmdir root

For those curious or wary, this script takes three parameters

  1. The location of SD card in the PC’s device tree
  2. SSID of your WiFi router
  3. Password for the WiFi router

It then mounts the SD card, accesses the files needed to setup WiFi, and inserts the connection information appropriately.

Thanks again, Stasiana.

Let’s keep going.

Before we can run the script it must be given executable permissions.

chmod +x

Note: If you execute the script in the same path as where you built the SD card then the script will complain

mkdir: cannot create directory ‘root’: File exists

That’s because the Arch Linux instructions didn’t mention removing the SD card paths.

To delete the paths root and boot which were required for setup run (make sure your not in the / path first).

sudo rm -R boot root

Now, let’s execute it, passing /dev/sdX, your_wifi_name, and your_wifi_password. Like so.

./ /dev/sdb wifi_name wifi_password

If all goes well, you should see.

Cleaning up

Anything else, leave me a comment and I’ll help troubleshoot.

3. Connecting

Ok! That’s it. Now, put the SD card into the rp0w and fire it up. The green light should begin flashing.

The last tricky part is knowing what IP address has been assigned to the rp0w on boot. After waiting a few minutes for it to connect to the wifi, visit your router’s admin page. It’s usually

You’ll need the router login information. But once in there should ba a section like “Attached Devices”. In there you should see an entry for “alarm” (which stands for Arch Linux ARM). This your rp0w.

Now, at the command line type:


Replacing the xs with the address of your Raspberry Pi. If you don’t know the address of the Raspberry Pi you can log into router and look for ALARMPI.

Where the xxx is the address assigned to the Pi. You should be prompted with an EDSCA warning (say yes). Then, you will need to enter the password which is alarm.

Welcome to Arch Linux ARM

         IRC: #archlinux-arm on
Last login: Thu Apr 12 12:18:05 2018 from
[alarm@alarmpi ~]$

Happy Arching.

Lumi5 – AVR / Arduino Wireless Uploader

This article will focus on setting up the Bluetooth 4.0 module, preparing the uploader, and uploading sketches over Bluetooth 4.0


This article builds off:

Upload Arduino Sketches to ATtiny85 with UART

The above article will need to be followed to prepare your ATtiny85 and Windows computer.

  1. ATtiny85 (or any ATtiny or ATmega chip)
  2. Arduino Uno (or compatible, this will be used once to burn the bootloader to the ATtiny85).
  3. FTDI (or compatible UART).
  4. Lumi (a browser based uploader for TinySafeBoot).
  5. Arduino IDE
  7. HM-10 or HM-11 Breakout*
  8. 1k Resistor
  9. Soldering iron

*Note: There are much cheaper versions of the breakout listed on eBay, but beware, there are many clones which will not work with this project. The one I’ve listed I’ve verified as working. Of course, I always recommend you roll your own breakout :)


Over-the-air uploading of programs to embedded devices is one of the more useful implementations in the history of embedded hardware. It allows the post-production and delivery of gadgets to have their behavior tweaked in reaction to end-user feedback.

Likewise, the Arduino phenomenon probably needs little explanation in this venue. However, I’ve personally not found many solutions for over-the-air uploading of Arduino sketches which have acceptable trade-offs. This article, along with the preceding, are nothing more than attempts to share what I’ve found.


This is simply for those who are curious on how this works, it may be skipped.

This tutorial will show you how to wirelessly upload Arduino sketches to an ATtiny85 over a PC’s built in Bluetooth 4.0 hardware and an HM-10. The concept is fairly simple.

The HM-10 has firmware which allow it to monitor all incoming serial data for AT commands. It can then intercept these commands from the stream, allowing the user to remotely control the behavior of the HM-10. In short, the HM-10 allows you to send a command to send a pin HIGH or LOW.

Now, the TinySafeBootloader is a serial bootloader for the ATtiny and ATmega AVR chip sets (these are the heart of Arduino). The most important difference between the TinySafeBootloader and others is it allows serial control over the bootloader behavior, this allows programmers to write uploader software for it, which is what I’ve done for this project.

To the point, we will wire up an HM-10 and an ATtiny with the TSB bootloader. One of the HM-10’s IO pins will be connected to the ATtiny’s RESET line. This will allow us to send a command to the remote HM-10, sending the ATtiny85 into bootloader mode, upload a sketch over the Bluetooth connection, then send another command to the HM-10 to send the ATtiny85 back into program execution mode.


Select a Pin

You will need to select a IO pin on the HM-10 to connect the 1k resistor. Any of the pins listed above should be supported.

Solder the 1k Resistor

After you have selected the IO pin you would like to use, solder a 1k resistor to the castellated (the half-via leads) on the HM-10.

A few notes:

This is not as hard as it looks; believe in yourself Flux and a fine-tip iron are helpful, but required Tweezers allow you to work angles. Tacky-putty helps hold the board in place, leaving both hands free to hold components.

Configure the HM-10 Firmware for Remote Uploading

Before the uploader will work, the HM-10 must be set as a peripheral device and remote transmission mode.

  1. Connect the FTDI chip to the HM-10 breakout, as shown.
  2. Open the Arduino IDE.
  3. Select the COM port connected to your FTDI chip
  4. Open the Serial terminal
  5. Make sure the terminal is set to, “No line ending”
  6. Type AT+ROLE0 and hit “Send”. The response “OK+Set:0” come up.
  7. Now, type AT+MODE2 and hit “Send. The response should be “OK+Set:2”.


  1. You can set the HM-10’s baud rate up to 57,600 (it gets iffy above this rate). This allows the bootloader to run more quickly. Here are the commands:
    • AT+BAUD0 = 9600
    • AT+BAUD1 = 19200
    • AT+BAUD2 = 38400
    • AT+BAUD3 = 57600
  2. You can change the name of your device using “AT+NAMExxxxxxxx”.

Wire It Up

The following connections will need to be made:

VCC should be 5V

Compile a Sketch to Upload

In the Arduino IDE, write a program you want to upload to your wireless TSB setup. However, instead of using the Arduino to upload the file, we are going to use it only to compile. To do this, go to Sketch -> Export Compiled Binary Then, select Sketch -> Show Sketch Folder. In this folder there will be two .HEX files. You want the one which does not have “…_with_bootloader.ino”. (Since, we already have a bootloader installed on our ATtiny.)

Connect to the HM-10 Module from Lumi5

I’ve provided a web based uploader for TinySafeBoot. A couple of notes, it only works in Chrome on Linux and Mac.

Before attempting to use the uploader, please open Chrome and type:


To get Chrome’s “experimental” Bluetooth LE web API to work we have to turn it on. Go ahead and select Enable.


Go ahead and navigate to:

This is the work-in-progress web based TinySafeBoot loader I’ve been working on. The HM-10 uploader is functional, though.

Turn on your HM-10. Select Bluetooth from the dropdown box and hit Search. If all has gone well, then you will see the name of your HM-10 module listed. Go ahead and connect to it.

Now, in the Pin # box, type the pin number corresponding with the pin on your HM-10 which you are using to reset the TinySafeBootloader.

Select the multi-gear button. This should give you a bootloader handshake button. Try it. It may take a couple of tries, but you should see the bootloader respond with the device signature and memory information.

If all went well, go ahead and click the Upload File button. Select the compiled HEX file from the Compile a Sketch step and hit upload.

Cross your fingers. It should respond with tell you it has written the file.

Important: Go ahead and refresh the webpage and reconnect. Once you’ve reconnected, the program you uploaded should be running.

We setup the HM-10 to hold the reset line low when the module is not connected, this is to save power. But it can be frustrating if your unaware of the setup, as it’s difficult to troubleshoot. “Why the heck isn’t the program running!?”

Please, please be kind and provide feedback. This is a work-in-progress, using a lot of custom hardware and experimental code.

List any questions below and I’ll try to troubleshoot ASAP.