Reyax RYWB116_Lite Beginners Guide Part I: How to configure a WLAN Access Point + a TCP/HTTP Server

RYWB116_Lite breakout board with an RYWB116 module.

Reyax’s RYWB116_Lite is a breakout board featuring the RYWB116 module. The RYWB116 module is based on Redpine Signals’ RS9116 technology and features a multi-protocol wireless stack including WLAN 802.11 b/g/n and Bluetooth 5. The breakout board simplifies to harness the RYWB116’s capabilities due to ready-to-use connection pins for serial communication. This beginners guide gives a brief introduction into the capabilities of the RYWB116. Moreover, it shows how to setup a WLAN Access Point by sending commands via a serial communication. Finally, a TCP server is created that could be used as HTTP web server.

Official Website: (Specs, datahseet, etc.)

– Reyax RYWB116_Lite [Search on Aliexpress | Amazon]

RYWB116_Lite/RYWB116 Capabilities + Pin layout

The RYWB116 module support multiple WLAN (IEEE 802.11b, 802.11g, 802.11n) and Bluetooth (5, 2.1+ EDR, LE, LE 2 Mbps) standards. The module can run in different modes, e.g.:

  • Wi-Fi Access Point (with support for up to 8 clients)
  • Wi-Fi Client
  • Wi-Fi Direct
  • Bluetooth Classic (EDR v 2.1)
  • Wi-Fi Client + Bluetooth Low Energy

Later in this guide, we will utilize the “Wi-Fi Access Point”-mode and start a TCP server.

The following table shows the most import specification values of the RYWB116_Lite breakout board (not RYWB116, e.g. differences between RYWB116_LITE and RYWB_116 in operating voltage):

Item NameMin. ValueTypical ValueMax. Value
Operating Voltage2.1V3.3V3.6V
Baud Rate9600115200921600
TX Mode Current400 mA
WLAN Data Transfer Average Current270 mA
WLAN Data Receive Average Current48.2 mA
Receive Power10 dBm
WLAN Operating Frequency2.412 GHz2.484 GHz
WLAN Bandwidth20/40 MHz
Bluetooth Operating Frequency2.402 GHz2.480 GHz
Bluetooth Data Rates125 Kbps3000 Kbps
Operating Temperature-40 °C+25 °C+85 °C
Source: RYWB116_Lite Data Sheet (Link to original PDF)
RYWB116 Pins.

The RYWB116_Lite has six pins which makes it easy to connect the breakout board with a microcontroller, such as the Arudino or STM32. Of course, it is also possible to connect it with an ESP8266 or ESP32. However, as both of them already come with ready-to-use wireless technology, it depends on the use case whether it makes sense or not. When connecting the RYWB116_Lite to a microcontroller, keep in mind that the operating voltage is 3.3V. If a 5V signal is connected to the breakout board, the board might be damaged. The following table gives more details on the pin layout:

Pin NameInput/OutputDescription
VBATTInputInput supply voltage (2.1-3.6V).
RST (RESET_N)InputActive-low reset asynchronous reset signal.
RXD (UART1_RX)InputUART 1 interface serial input.
TXD (UART1_TX)OutputUART 1 interface serial output.
WUP (BL_HOST_CMD_BYP/ULP_WAKEUP_IN)InputThis signal has two functionalities – one during the bootloading process and one after the bootloading. During bootloading, this signal is an active-high input to indicate that the bootloader should bypass any inputs from the Host processor and continue to load the default firmware from Flash. After bootloading, this signal is an active-high input to indicate that the chip/module should wakeup from its Ultra Low Power (ULP) sleep mode.
Source: RYWB116_Lite Data Sheet (Link to original PDF)

How to send commands to the RYWB116 module via Serial Connection: Hardware setup

USB-to-TTL Device in 3.3V mode.

In order to send commands to the RYWB116 module the breakout board has to be connected to a PC. This can be achieved by so-called “USB-to-TTL” devices (come with different names, e.g. USB-to-Serial) that can be plugged in like normal USB devices. Moreover, these “USB-to-TTL” devices have pins that can be connected to the pins of the RYWB116_Lite breakout board. There exist different variants of “USB-to-TTL” devices. The one that I own support 3.3V and 5V operating voltage modes. In order to activate 3.3V mode (and deactivate 5V mode) a jumper has to be set.

– USB-to-TTL device [Search on Aliexpress | Amazon]

USB-to-TTL device wired to RYWB116_Lite breakout board.

In order to make it work, the USB-to-TTL device and the RYWB116_Lite have to be wired correctly. Besides the correct voltage supply, the RX and TX pins have to wired in order that one’s TX pin (transmit) goes to the other’s RX pin (receive). If you have any doubts, whether your device has the correct voltage level, check with a multi meter. The following table shows how I connected my device to the breakout board:

USB-to-TTL PinRYWB116_Lite Pin
5Vno connection / yellow jumper with USB-TO-TTL VCC
VCCno connection / yellow jumper with USB-TO-TTL 5V
How to wire a USB-to-TTL device to an RYWB breakout board to establish a serial connection.

If the USB-to-TTL device is connected to a USB port of the PC, the PC is ready to send signals to the RYWB116 module from the hardware’s point of view. Some USB-to-TTL devices require special drivers: In my case, I had to install a driver called “CH340/CH341 USB to serial port”, before I could work with the device.

How to send commands to the RYWB116 module via Serial Connection: Software setup

HTerm v.0.8.5 after staring (“no serial connection traffic yet”)

Besides the setting up the hardware, a so-called “terminal program” software is required to send the commands to the module. There are many terminal programs available. At time of writing, the one I like most is HTerm from Tobias Hammer. HTerm has all functions that we need to conveniently configure and command the RYWB116 module:

  • Supports all available hardware and virtual USB serial rs232 ports
  • Supports all baud rates provided by the port + Parity and flow-control
  • Input and output in ASCII, hexadecimal, binary and decimal
  • Support for predefined command sequences (XML configuration files)

This beginner guide does not cover an introduction into HTerm or terminal programs in general. Therefore, if you have never used a terminal program before and/or are struggling with the following the remaining part of the guide, search the Internet for some basic tutorials on terminal programs. Here are some additional hints:

  • With HTerm, you can send data in different formats (ASCII text, HEXADECIMAL, DECIMAL). It can make sense to switch between formats when entering data.
  • Moreover, HTerm has many options on how to represent data that has been sent or received (different formats, when to show a new line,…). I strongly recommend to play around with these options in order to find a representation configuration that seems convenient to you.
  • If you do not get any response from the device anymore, remove it from the USB port and plug it in again.

How to send commands to the RYWB116 module via Serial Connection: Tutorial

Please keep in mind that the following instructions does not cover any error handling and other additional aspects. The idea is to show the fastest way to create a TCP server.

As a first step, we have to establish a connection to the RYWB116 module via the USB-to-TTL device. HTerm shows you all available ports (Windows: COM1, COM2, COM3,…) that are ready to be connected. If you have some additional serial devices connected to your PC, multiple ports will show up. The easiest solution to identify the port of the USB-to-TTL device might be to just plug it out, have a look which port is missing, and then plug it in again, and then, the missing port should reappear. As shown in the specification values table, the RYWB116 support different baud rate values. The standard parameter to establish a serial connection are: baudrate 115200, data bits per character 8, stop bits 1, parity none, and flow control none.

When starting the serial connection, a sequence of commands has to be send to the RYWB116 module. HTerm support to import predefined sequences files, so that a user can easily send commands that have been defined in the imported sequence file. In the following, you find an HTerm sequence file that contains the commands used in the next part of this guide. To import the sequence file, save the content as XML and import it into your HTerm Sequence Overview.

        <SequenceItem name="Baudrate Detection - Start">
            <sequence value="h[1C]" />
        <SequenceItem name="Baudrate Detection - End">
            <sequence value="h[55]" />
        <SequenceItem name="Firmware - Select Default Firmware (1)">
            <description>Load defualt firmware</description>
            <sequence value="h[31]" />
        <SequenceItem name="Init - Operation Mode">
            <description>This command sets the operating mode of the module.</description>
            <sequence value="a[at+rsi_opermode=6,1,48,0] h[0D] h[0A]" />
        <SequenceItem name="Init - Band">
            <description>This command sets the operating band of the module.</description>
            <sequence value="a[at+rsi_band=0] h[0D] h[0A]" />
        <SequenceItem name="Init - Initialization">
            <description>This command initializes the module.</description>
            <sequence value="a[at+rsi_init] h[0D] h[0A]" />
        <SequenceItem name="Config - IP Configuration">
            <description>This command can be used optionally in this flow to configure the IP ( in this example) of the AP. If this command is not issued, a default IP of will be used.</description>
            <sequence value="a[at+rsi_ipconf=0,,,] h[0D] h[0A]" />
        <SequenceItem name="Config - AP Configuration">
            <description>This command will configure the SSID of the AP to “REYAX” and password will be set to “12345678”.</description>
            <sequence value="a[at+rsi_apconf=1,REYAX,2,2,12345678,300,2,4] h[0D] h[0A]" />
        <SequenceItem name="Start AP">
            <description>This command will create the Access Point with SSID reyax where xy is a pair of alphanumeric character.
A client device (Named "Device A" in this example) can now associate to the AP, open sockets and transfer data.</description>
            <sequence value="a[at+rsi_join=REYAX,0,2] h[0D] h[0A]" />
        <SequenceItem name="Start Server">
            <description>Opens a server TCP socket inside the module with port number 5001 with tos (type ofservice) type 0 and max clients support count as 5. A client socket at the remote node (Device A) can connect to the server socket</description>
            <sequence value="a[at+rsi_ltcp=5001,5,0] h[0D] h[0A]" />

Let’s start with sending some commands. As an initial step, an auto baud rate detection (ADBR) procedure takes please. We start this procedure by sending 0x1C to the module. The module responds with 0x55. Then, we sent also 0x55 to the module which completes the procedure.

< 0x1C
> 0x55
< 0x55

Next, the module boots up and shows a welcome message (bootloader menu):

We select options “1” (Load Default Wireless Firmware) by sending “1” as ASCII text (in the following, commands are send in ASCII format denoted with “”).

< "1"

Now, we can start to configure the Access Point that we want to open up with the RYWB116 module. First, we configure the operation mode to access point mode (first parameter: 6=Access Point Mode, see more details in specification):

< "at+rsi_opermode=6,1,48,0\r\n"
> "OK\r\n"

Next, we set the band (0=2.4Ghz):

< "at+rsi_band=0\r\n"
> "OK\r\n"

Next step is to initialize the module:

< "at+rsi_init\r\n"
> "OK< macAddr >\r\n"

Now, the module is initialized. In addition to an “OK”-confirmation, the module reponds with its MAC address (might result in cryptic characters if received data is shown as ASCII text):

The next step is to configure the IP ( in this example) of the AP. If this command is not issued, a default IP of will be used. The module will respond with the MAC address, IP address, net mask and gateway IP:

< "at+rsi_ipconf=0,,,\r\n"
> "OK< macAddr >< ipaddr >< netmask >< gateway >\r\n"

Next, the Access Point’s name, password etc. is configured. In this guide, we open an Access Point with the name “REYAX” and password “12345678”.

< "at+rsi_apconf=1,REYAX,2,2,12345678,300,2,4\r\n"
> "OK\r\n"

Now, the Access Point can be started. If the start was successful, the access point should show up in the list of accessible WLAN networks:

< "at+rsi_join=REYAX,0,2\r\n"
> "OK<GO status>\r\n"

As a final step, we start a TCP server on port 5001 where up to 5 clients are able to connect to:

< "at+rsi_ltcp=5001,5,0\r\n"

When we have sent the command, we can use our PC or mobile device, connect to the WLAN “REYAX”, open up a web browser and enter the URL “”. If we do so, the module will send the text of the HTTP Request.

And that’s it!

Video tutorial

Playing around with the Grove Beginner Kit for Arduino

Recently, I got my hands on a Grove Beginner Kit for Arduino. Although I have heard of the Grove ecosystem before, it has been the first time I actually tried it out.

– Grove Beginner Kit for Arduino [Search on Aliexpress | Amazon]

What is Grove?

Grove Beginner Kit for Arduino.

Grove is kind of a electronics ecosystem coming from Seeed Studio. Probably, the primary target audience of Grove are beginners that are looking for something more simple than starting with a plain Arduino and a set of sensor/actuator modules from the shelf. If you go for a plain Arduino and a set of modules, you are sometimes facing the problem that you do not know how to wire a module to an Arduino. Moreover, if you are searching the Internet for a specific piece of code for your module, it can happen that it simply does not work. Either it has not been the right piece of code, the module version is not exactly the same, or the manufacturer changed something without changing the name or version of the module.

On the one hand, I think struggling around with such problems (and solving them) helps to get a better understanding of electronics. On the other hand, I can really share the pain when reading my readers’ comments or mails that the code or wiring is not working because it is not the same module. If you are a beginner and do not know how to proceed then, this can become very frustrating. With Grove, all modules come from the same vendor — or are at least a little bit more standardized than ‘normal modules’. So you are running less likely in wiring or code mismatch problems.

What is Grove on a more technical level?

Grove cables/wires (4 Pins) are used to connect Grove base units with Grove modules.

The main components of the Grove ecosystem are the Grove base units, e.g. a Grove-compatible Arduino Uno, that are connected to Grove modules. Grove base units and modules come with special connectors, which have 4 pins. Base units and modules can be wired by Grove cables, which have 4 wires and fit on the special connectors. Depending of the specific module, the cable is used for a different type of connection (Analog, Digital, UART or I2C). In the following you find the pin/wiring setup that can be found on the Grove Beginner Kit:

Pin / WireFunctionComment
Pin 1 / YellowSIGAnalog signal
Pin 2 / WhiteNCNormally closed / unused
Pin 3 / RedVCC5V/3.3V voltage supply for module
Pin 4 / BlackGNDGround
Analog and digital connection type
Pin / WireFunctionComment
Pin 1 / YellowSCLI2C Clock
Pin 2 / WhiteSDAI2C Data
Pin 3 / RedVCC5V/3.3V voltage supply for module
Pin 4 / BlackGNDGround
I2C connection type

So, in a nutshell: Grove is a ecosystem with ‘standardized’ base units and modules + a simplified wiring scheme including special connectors and wires.

Grove Beginner Kit for Arduino

Grove Beginner Kit: Grove base unit.
Grove Beginner Kit: OLED Display 0.96″ Module

The Grove beginner kit is a box (yes, the packaging is part of the kit) with a Grove-compatible Arduino and 10 modules integrated on a single board. ‘Integrated’ means that you do not need the Grove cables, since all modules are already ‘PCB-wired’ to the Arduino. The idea is that you are free to choose whether you want the board as it is or, alternatively, ‘break out’ the modules and then you need the wires to get them connected again. As I like the concept of having an all-in-one-board, I just left everything as is. Well noted is that the beginner kit is ready-to-use, besides some Grove wires it comes also with a micro USB cable. In addition, Seeed Studio published a PDF with about 12 code examples for the beginner kit.

In the following, an example is provided that is not part of the PDF. Instead, I created a new example that makes use of multiple modules at the same time. I chose such an example, as I receive a lot of messages from beginners that are often struggling with combining multiple modules.

Example: Light Intensity Alarm

The idea of this example is to control the LED and Buzzer based on how much light is detected by the Light sensor. The higher the amount of light, the higher the frequency of the LED blinking and the Buzzer turning on/off. Moreover, the Buzzer is only active if the Button is pressed. The OLED display shows the currently detected light and the corresponding frequency used for the LED and Buzzer module.

As all modules are already connected to the Arduino, we can directly start with the programming. The code requires an external library (U8g2) which is required to control the display module (Grove OLED Display 0.96″). When I was playing around with my beginner kit, I also used some other libraries for the display module. But in the end, the U8g2 worked best and is also used in the official examples provided by Seeed Studio.

Regarding the light sensor values, I chose 750 as a maximum value as it was a good fit to my lighting conditions. Here, you are totally free to use a different maximum value.

MIT License
Copyright 2020 Michael Schoeffler (

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.


Explanation: The idea of this example is to control the LED and Buzzer based on how much light is detected by the Light sensor. 
The higher the amount of light, the higher the frequency of the LED blinking and the Buzzer turning on. 
Moreover, the Buzzer is only active if the Button is pressed. 
The OLED display shows the currently detected light and the corresponding frequency used for LED and Buzzer.

#include <U8x8lib.h> // Header file is part of the required U8g2 library. Arduino IDE: Install U8g2 library --> Go to "Tools", then "Manage Libraries.".

#define LED 4 // LED is on Digital Pin 4 of Grove Beginner Kit
#define Buzzer 5 // Buzzer is on Digital Pin 5 of Grove Beginner Kit
#define Button 6 // Button is on Digital Pin 6 of Grove Beginner Kit
#define Light A6 // Light sensor is on Analog Pin 6 of Grove Beginner Kit

U8X8_SSD1306_128X64_ALT0_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE); // Part of the initalization of the OLED Display 0.96" 

bool toggle = false; // This variable represents the toggle state of the LED and Buzzer

void setup() {
  pinMode(LED, OUTPUT); // LED is an output
  digitalWrite(LED, LOW); // LED is set to zero --> "out"
  pinMode(Buzzer, OUTPUT); // Buzzer is also an output
  digitalWrite(LED, LOW); // Buzzer is set to zero --> "out"
  pinMode(Button, INPUT); // Button is an input
  pinMode(Light, INPUT); // Light sensor is also an input

  u8x8.begin(); // Display is now ready to use
  u8x8.setFlipMode(1); // Technically, the display is mounted upside down on the Grove module. Therefore, we flip it.
  u8x8.setFont(u8x8_font_chroma48medium8_r); // font type 

void loop() {
  int light_measured = analogRead(Light); // Light is measured
  int frequency = map(light_measured, 0, 750, 1000, 10); // Amount of light is converted to the turn on/off frequency of the LED (and buzzer). 
  // Additional explanation: No light (sensor value: 0) will switch on/off the LED each 1000ms
  // A lot of light (sensor value => 750) will  switch on/off the LED each 10ms ("LED starts flickering")

  u8x8.clear(); // Display is cleared in each step to remove old values
  u8x8.setCursor(0, 0); // Cursor of display is set to first position (first row)
  String light = "Light: " + String(light_measured); // String with measured light value
  u8x8.print(light.c_str()); // string is printed to display

  u8x8.setCursor(0, 1); // Cursor is set to the second row 
  String freq = "Frequency: " + String(frequency);

  digitalWrite(LED, toggle); // The LED is turned on/off based on the current value of the toggle variable
  if (digitalRead(Button) == HIGH) { // if the button is pressed, some additional code is executed (Buzzer)
    digitalWrite(Buzzer, toggle);  // Buzzer get's also a toggle value. This will result in a short audible 'click' (synchronized with the LED status)
  toggle = !toggle; // Toggle value is inverted; E.g. if toggle is LOW, the new value will be HIGH.
  delay(frequency); // The code is executed again after a waiting time that corresponds to the calculated frequency representation. 
  // Additional remark: The previous line is a minor weakness of the code, if the amount of light/frequency is low, the reaction time of the programm is also low as it is in 'delay' state
Example for the Grove Beginner Kit: Light-based alarm.

When the code is compiled and transferred to the Arduino on the Beginner Kit, the LED should start blinking based on the amount of light. For example, if you hold your hand over the Light sensor (so that no light is captured at all), the LED should blink very slowly. If you hold the beginner kit towards the sun (or another light source), the LED should start blinking much faster. The Display module should show the current values of the light sensor as well as the calculated frequency. If you press the button, the Buzzer should make clicks in the same pattern as the LED blinks.

If you read the code carefully, you might have recognized that the frequency implementation is not perfect. The frequency pattern is implement with a delay function which is a drawback for the program’s reaction time if the amount of light changes form little to a lot. Nonetheless, I chose this implementation as I thought it is a good trade-off between functionality and understandability of the code.

Overall, I think the Grove ecosystem is a good idea and well suited for people who do want to start as smoothly as possible. Naturally, in comparison to using a ‘normal’ Arduino and modules, the prices of Grove components are a bit higher. But in return, you get a full documentation for each module from Seeed Studio + an ‘implicit guarantee’ that the module will work with your base unit and code example. Moreover, the quality of the Grove components is very good — at least what I have seen from the modules on my beginners kit.


Beginners Guide on how to start with the Arduino Ecosystem + electronics

I’m in a lucky position that this blog ranks very well on search engines which leads to a high amount of daily visitors. In addition, I have a lot of beginner tutorials on this blog. As a result, I get a lot of emails, many of them are about how to get started with Arduino, sensor/actuators, etc.
Unfortunately, I get many more emails/messages than I can answer. That is why I started this article to address some of these questions. This article has been written especially for those readers who are just thinking about starting with the Arduino ecosystem or electronics (e.g. ESP8266, ESP32,…).

Why do you want to start with Arduino/Electronics?

Before you start, maybe think about why you want to start with electronics. Besides my professional life, I acquired some skills in other disciplines, such as construction knowledge, woodworking, 3D printing etc. If I order all these disciplines by how often I really need to apply them, then electronics is maybe the one I could spare most. Don’t get me wrong, in my spare time, I do often electronics and I like it… but it would not hurt me in terms of life quality or financial savings if I just stop. For example, we are currently refurbishing our house, so my construction skills come in handy. In addition, I build many furnishings (table, lamps, shelves,…) myself, so I do also woodworking a lot. I guess, all the money and time I spent to improve my construction and woodworking capabilities (e.g. by buying new tools) did payback multiple times in improved life quality and/or financial savings.

I’m note sure if this applies also to my activities related to electronics and the Arduino ecosystem. The main reason why I started was to deepen my knowledge in sensors and actuators. My main background has been computer science with a strong focus on software architecture/engineering/development – but I always intended to go into “hardware engineering”, too. So, when I started with electronics, it had already been clear to me, that gaining knowledge in electronics will be part of my life for a long time. Therefore, I probably spent a bit more money on my “initial equipment” than I this article will suggest.

Getting to the point: the recommendations in this article address especially readers that do not know for sure if they will stick with electronics for a longer time frame. In my opinion, in such cases it makes no sense to spent a lot of money to get started. The good news is that starting with the Arduino ecosystem is not very cost-intense. There are excellent starter kits available for less than 25$. If you already know that you will stick with electronics for some time – like in my case, then it can make sense to spend a bit more on a starter kit.

What do you, as a person, need to start?

Naturally, it will be beneficial if you have already acquired some basic knowledge in electronics. Moreover, it also helps if you have someone around that can help you if you get stucked (e.g. uncle or aunt who is familiar with the basics of electronics). Nonetheless, if you have no experience at all, but are motivated enough to learn the ropes, you will very likely succeed. There are so many video or written tutorials around, so there is almost no excuse to not get started.

What software do you need?

Screenshot of the Arduino IDE.

The Arduino IDE is basically everything you need to get started. Fortunately, the Arduino IDE is available for free. There are very good alternatives available that are also for free, e.g. PlatformIO. But I recommend to start with the Arduino IDE, as its widely used and very easy to understand. If you are missing some advanced features, you can always go to another software platform.

What hardware equipment do you need?

As a starting point I recommend to aquire a breadboard, some jumper wires and, of course, an Arduino. In addition it makes sense to buy also some resistors and LEDs and a switch in order to implement very basic use cases.

Basic equipment to start with:
– Breadboard [Search on Aliexpress | Amazon]
– Jumper wires [Search on Aliexpress | Amazon]
– Arduino [Search on Aliexpress | Amazon]
– LED [Search on Aliexpress | Amazon]
– Switch [Search on Aliexpress | Amazon]
– Resistor (1k ohm) [Search on Aliexpress | Amazon]


Breadboards are perfect prototyping platforms for making circuits. The main advantage of breadboards is that soldering is not required in order to integrate components into a circuit. In particular, many components can be just placed onto a breadboards. This makes breadboards the perfect choice for prototyping or testing (new) components. The drawback is that breadboards are not the best solution when it comes to permanent applications. Especially, if the breadboard gets moved or shaken around, components might come loose. Nonetheless, they are the perfect starting point for beginners.

When buying some breadboards, I don’t recommend to buy cheapest ones. Once or twice I bought a batch of breadboards for a very low price. Unfortunately, the quality turned out to be not really sufficient, as wires and legs of components did not stick well into the exposed holes of these breadboards. Therefore, I recommend to buy rather 3-5 more expensive breadboards (or with good customer ratings) than 10 cheap breadboards. Starting with one breadboard is also fine. But, in order to preserve a project for some time, it makes sense to have multiple breadboards.

In most cases, the standard size of about 5.5 cm x 8.5 cm / 2.2″ x 3.4″ is fine. Though, I recommend to have at least one larger breadboard (5.5 cm x 17 cm / 2.2″ x 7″).

Jumper Wires

Jumper wires (also called jump wires) are electrical wires with connector at each ends. There exists three types of different jumper wires in terms of their connectors: female/female, male/male, and female/male. The male connectors fit perfectly into the exposed holes of breadboards. Jumper wires are the connection elements when it comes to implement a circuit on a breadboard. In other words, jumper wires are used to connect electrical components in order to form a circuit.

Jumper wires are available in different lengths, where lengths of 10 cm and 20 cm are very common. In many tutorials – also in mine, black is used for GND and red for 5V. When you plan to use the same color scheme and start to wire, you will shortly recognize that you need black and red more often than the other colors. For this reason, I bought extra batches of just black and red wires.

Arduino (+USB cable)

Three different variants of the Arduino Uno R3: Noname Arduino Uno R3, Elegoo Uno R3 and Seeeduino

I guess, for many people, Arduino is just the Arduino Uno R3 Single-board microcontroller. But actually, there is more to it: there is the integrated development editor (IDE), the company Arduino LLC, the website, etc. Therefore, I see Arduino primarily as an ecosystem with a hardware platform targeting on prototype projects.

Anyway, to get started you need to pick a hardware: There exists many variants, such as the popular Arduino Uno R3, Arduino Nano, Arduino Mini etc. When starting, I would go for a “normal” Arduino Uno R3. The reason is that it is widely used an especially beginners do not get confused when copying/adapting from existing wiring diagrams.

If you decided for an Uno R3, the question is which one to buy? I own around ten Arduino Uno R3s ranging from 1$ (noname from Aliexpress) to around 25$ (original Arduino). The “original” Arduino made in Italy is for sure of high quality, but also very expensive. Especially, when considering the Arduino Uno R3 is almost 10 years old. From a price point, I recommend Arduinos in a price range between 7-15$. If you plan to use only the analog inputs and digital inputs/outputs, then you are probably fine with a cheap Arduino. At least I had many problems with cheap Arduinos for application in which SPI, I2C or serial connections were used.

Besides the Arduino Uno R3 type, I can also recommend the Arduino Nanos. They are very compatible with the Arduino Unos and they fit perfectly on a breadboard. Especially, if you do some prototyping over a longer time frame, it makes sense to use a Nano in order to have everything on a single breadboard. Btw. make sure that the Arduino comes also with a USB cable if you don’t have one at home. The Arduino Uno has a USB B input and the Nano has a USB Mini input.

LED + 1k Resistor

Resistors (1k Ohm) + LEDs in different colors.

LEDs (light-emitting diodes) are perfect when you need output components that you want to control with an Arduino. LEDs can only emit light if wired correctly: GND is connected to the cathode (short leg) and the 5V signal to the anode (long leg). Moreover, LEDs require a resistor. You can either use Ohm’s Law or stick with the 1k rule of thumb (“1k works always, except in 1 out of 1000 cases”). If you are interested in more details, I made a tutorial about how to let an LED blink with an Arduino Uno.


Switches are perfect as “input components” for starters. They are very simple to use as pressing a switch will result in a HIGH signal and releasing the switch in a LOW signal (sometimes it’s also the other way around). For the first batch of switches, I recommend the ones that fit directly on a breadboard. The main advantage is that you can use them without soldering.

Starter Kits

Arduino Starter Kit (Elegoo Basic Starter Kit)

There are a lot of starter kits available which contain all of the previous components. Some days ago, Elegoo gave me their “Basic Starter Kit” for testing. In my opinion, it has a very good price-quality ratio and has contains the most important components. For these reasons, I can honestly recommend the starter kit (please see my Sponsorship Disclosure).

– Arduino starter kit [Search on Aliexpress | Amazon]
– Elegoo Basic Starter Kit [Search on Aliexpress | Amazon]

I did the basics, what’s next?

Selection of different breakout boards that can be directly wired to an Arduino, e.g. with a jumper wire.

If you started with a basic kit and are asking for more now, or want to start more ambitious direct from the beginning, I recommend to just go for more sensors and actuators. In many cases, it is not possible to wire a sensor directly to an Arduino since additional hardware is required. Luckily, there exist tons of breakout boards which contain the sensor/actuator and the required hardware components as well. Often, breakout boards are also referred to as “name of sensor/actuator”-module, especially when searching them up on online shops. There are a lot of breakout boards available that can directly wired to an Arduino. If you look up my tutorials you can find many of them: For example, KMR-1.8 SPI (TFT Display), RDM630/RDM6300 (RFID Reader), GY-521/MPU-6050 (gyroscope+accelerometer).

Instead of buying each component individually, there is also the possibility to buy a kit with multiple sensor/actuator components. When I started with this topics some years ago, one of the first things I bought was also a kit with around 25 breakout boards.

– Arduino sensor kit [Search on Aliexpress | Amazon]

What about debugging?

Multimeter (left) and low-cost DSO138 oscilloscope (right).

You will often come to point where you think that you code and wiring is correct but your application just does not work. Then it makes sense to find the cause (also referred to as bug) and to do some debugging.

The most basic approach is to use the Serial.print() function and add “print outs” to the code. This helps you to find software related problems, e.g. to check how often a for-loop has been executed etc.

Finding a problem related to the hardware can be sometimes more complicated. The probably most typical mistake is that the wiring is not correct. For example, if you mix up a 3.3V input of a component with a 5V signal, the components gets damaged, and then good luck finding the problem. A good start is always using a multimeter to check whether the voltage levels are as expected. Sometimes, an application is expected to change the voltage level based on a specific pattern, e.g. a LED is supposed to light up for two seconds, go out for two seconds, light up for to two seconds and so on. In such cases, oscilloscopes are the perfect tool as they can monitor the timing behaviour. Unfortunately, oscilloscopes are also very expensive. Luckily, there is the so-called DSO138 oscilloscope which is, in my opinion, the best oscilloscope in terms of quality-price-ratio. The DSO138 can be purchased for around 10-20$ but has everything you need for debugging typical circuits of Arduino applications.

– Multimeter [Search on Aliexpress | Amazon]
– DSO138 oscilloscope [Search on Aliexpress | Amazon]

What are alternatives to Arduino?

ESP32 development board.

Since the start of the Arduino ecosystem more than ten years have been passed. But I think it is still one of the best systems for starting with electronics. If you look up for alternatives, you probably come across recommendations for ESP8266 and ESP32. The ESP8266 is a low-cost Wi-Fi microchip. In comparison to the Arduino Uno R3, it enables you to connect to a wireless network. Whenever you want to push data to a network or the Internet, the ESP8266 is a good choice. The ESP32 can be seen as a successor to the ESP8266 (more features, more performance, etc.). The good thing about ESP8266 and ESP32 is that they are very compatible to the Arduino ecosystem. For example, you can also use the Arduino IDE to program them. Additionally, a lot of libraries written for the Arduino do also work for ESP8266 and ESP32. If you know right from the start that you want to implement wireless application, that maybe go for a ESP8266 or ESP32. Alternatively, when you have acquired some experience with the “plain” Arduinos, the ESP8266 and ESP32 are the perfect platforms “for taking the next step”.

Quick video guide about how to get started with the Arduino ecosystem

Arduino-to-Arduino Communication (via Serial Connection)

This tutorial shows how to establish a serial connection connection from an Arduino to another Arduino. Each Arduino has a tact switch and an LED. If an Arduino’s tactile switch is pressed, the LED of the other Arduino will be turned on.

List of materials (each item of this list is needed 2x):
– Jumper wires [Search on Aliexpress | Amazon]
– Breadboard [Search on Aliexpress | Amazon]
– Arduino [Search on Aliexpress | Amazon]
– Tactile switch [Search on Aliexpress | Amazon]
– LED [Search on Aliexpress | Amazon]
– Resistor (1k ohm) [Search on Aliexpress | Amazon]

Components used in this tutorial: 2x Arduinos, 2x tact switches, 2x 1k resistors and LEDs.
Components used in this tutorial: 2x Arduinos, 2x tact switches, 2x 1k resistors and LEDs.

In order to demonstrate the Arduino-to-Arduino communicaten two different types of Arduinos are used. The first Arduino is an Seeeduino Nano, which is a variant that is compatible to the Arduino Nano. The second Arduino is an Seeeduino V4.2 which is fully compatible to the Arduino Uno (see for more information). Both Arduinos will send only on of two bytes: Either ‘0’ or ‘1’. If ‘0’ has been transmitted, the LED will be turned off. If ‘1’ has been transmitted, the LED will be turned on.


Setup after performing the wiring.
Setup after performing the wiring.

Both Arduino setups have the basically the same wiring: a tact switch, an LED and a 1k resistor is added to a breaboard. The tact switch is wired to GND and two digital pin #2. The LED is wired to GND and the 1k resistor. The resistor is wired to digital pin #3. The serial connection requires three wires: for GND, receive and transmit. The receive wire is connected to digital pin #10. The transmit wire is connected to digital pin #11. In order to make the serial connection work, the receive wire of one Arduino is connected to the transmit wire of the other Arduino. The GND wire might not be needed, e.g. as both Arduinos probably share the same GND signal when powered from the same power supply. Nonetheless, it has been added to the wiring diagram for the sake of completeness.

Source code

First, the pins for the tact switch (INPUT_PULLUP) and LED (OUTPUT) are set up. Then, the serial connection is started. In the loop function, it is checked whether a byte was received from the serial connection. The digital output is set to the received byte: Either ‘0’/false to turn off the LED or ‘1’/true to turn it on. Next, the current status of the tact switch is sent. ‘0’ is sent to the other Arduino, if the tact switch if not pressed. Otherwise, ‘1’ is sent. At the end of the loop function, a delay of 250ms is added.

// (c) Michael Schoeffler 2020,

 * This program is part of a tutorial that shows how to communicate from an Arduino to another Arduino via a serial connection. 
 * The status of a tact switch is sent to the other Arduino. If the switch is pressed, an LED is turned on.
 * This program has to be transferred to both Arduinos in order to make the application work. 

#include <SoftwareSerial.h>

const int IN_TACT_SWITCH = 2; // input pin for tactile switch
const int OUT_LED = 3; // output pin for LED

SoftwareSerial sserial(10,11); // receive pin=10, transmit pin=11

void setup() {
  pinMode(IN_TACT_SWITCH, INPUT_PULLUP); // set the LED pin to input pullup mode
  pinMode(OUT_LED, OUTPUT); // set the LED pin to output mode
  sserial.begin(9600); // start serial connection

void loop() {

  // receive
  if (sserial.available() > 0) {
    byte incomingByte = 0;
    incomingByte =;
    if (incomingByte != -1) {
      digitalWrite(OUT_LED, incomingByte); // switch LED on or off 

  // send 
  sserial.write(!digitalRead(IN_TACT_SWITCH)); // Tact switch (used in the corresponding tutorial) is inverted. Therefore, inverted status is sent to other Arduino
  delay(250); // delay required to avoid flooding the other Arduino ("DoS attack")


The source code has to be transferred to both Arduinos (= both Arduinos have the same very same code). If everything has been executed correctly, an Arduino’s LED is switched on, if the tact switch of the other Arduino has been pressed.

Tact switch of Arduino Uno is pressed (background) >> Yellow LED of Arduino Nano is switched on.
Tact switch of Arduino Nano is pressed (background), Green LED of Arduino Uno is switched on.

Video tutorial

Control Fairy LED Strings with a Relay Module and an Arduino

This tutorial shows how to control so-called “fairy LED strings” (also called LED strings, fairy lights, fairy LEDs etc.) with an Arduino. The idea is to have multiple fairy LED strings that are switched on and off corresponding to a specific pattern. Typically, these strings are either powered with a battery pack or with a USB cable/power supply. Here, we focus on the USB-powered LEDs, since they are easier to get working with an Arduino. Arduinos are normally not able to provide enough power for such fairy stringy by their digital outputs. Therefore, we make also use of a relay modules in this tutorial.

List of materials:
– Jumper wires [Search on Aliexpress | Amazon]
– Breadboard [Search on Aliexpress | Amazon]
– Fairy LED strings   [Search on Aliexpress | Amazon]
– Arduino [Search on Aliexpress | Amazon]
– Relay Module   [Search on Aliexpress | Amazon]
– USB terminal adapter (female) [Search on Aliexpress | Amazon]


Fairy LED Strings.

Fairy LED Strings
I don’t know for sure, but fairy LED strings are maybe called fairy LEDs strings, as they look like tiny fairies when watching from some distance. In many stores, they are presented inside a jar which might remind on the fairies of the popular video game series “Zelda” (where fairies are often transported in jars by the main protagonist “Link”).
Normally, fairy LED strings consist of a chain of simple LEDs which are connected via insulated copper wire. In order to make the LEDs more robust, they are wrapped in some sort of dry hot glue.

Seeeduino Nano.

Arduino (Seeeduino Nano)
In this tutorial, I make use of a Seeeduino Nano from Seeedstudio which comes with a nicely red-colored PCB. Additionally, it’s compatible to a conventional Arduino Nano. In contrast to a normal Arduino Nano, it has a USB Type-C connector instead of a USB Mini connector. This might be an advantage if you own more USB Type-C cables than USB Mini cables (imho which are rarely distributed nowadays). In this tutorial, the Seeedunio Nano is used to “store the lightning program” and control the fairy LEDs strings according to this program.

Relay Module HL-545.

Relay module
In order to switch on an LED, the Seeeduino must bring 5V to the fairy LED string’s USB connector. To my knowledge, typical Arduinos are only able to provide 40mA via an digital output pin. Normally, this is not sufficient to power a string with multiple LEDs. In order to account for this, we utilize a relay module. A relay module (as used in this tutorial) exists in different variants: supporting 1-, 2-, 4-, 8 ports. In this tutorial, the HL-545 with 4 ports is used to power two fairy LED strings (yellow and blue).

USB Terminal Adapter.

USB Terminal Adapter
Now, the question is how to wire a USB-based fairy LED string to a breadboard / “jumper wire system”. For this use case, a female USB terminal adapter is quite useful. These adapters allow you to plug in an USB cable and, then, have the corresponding signal also on a terminal block. Luckily, jumper wires can be easily connected to the terminal blocks. In particular, we are interested to connect the GND/- and 5V/+ pins of the USB cable to two separate jumper wires.


In this setup exist two 5V circuits. The first one is for powering the fairy LED strings. The second one is for powering the Arduino and the relay module. It would also be possible to have only one 5V circuit which powers all components.

Wiring schematic.

Source code

The program is very basic since there is no special code for the relay module or for the fairy LED strings… it is more or less only setting two output pins. The idea of the program is to perform a little “light show”. In the first phase, no fairy LED strings is on. Then, only the yellow string is shown. Next, only the blue string is shown. Finally, both strings are shown. In addition, each phase is shown for two seconds.

In the setup function, digital pin 2 and 3 are declared as output pins. Then in the loop function, the “light show phases” are represented by setting the two outputs pin and adding a delay of two seconds. For example, if both LEDs are set, the output pins are set two LOW. Yes, LOW is correct as the relay model triggers on LOW signals!

// (c) Michael Schoeffler 2019,
// This program shows how to switch on two different "fairy LEDs" by utilizing a relay module. 
// In particular, the program resembles a small "lighting program":
// Both LEDs are off, only first LED is switched on, only the second LED is switched on, both LEDs are switched on.
// Each "light mode" shows up for two seconds.

void setup() {
  pinMode(2, OUTPUT); // Digital pin 2 is an output pin
  pinMode(3, OUTPUT); // Digital pin 3 is an output pin

void loop() {
  // keep in mind: relay module "switches on" if signal is LOW
  // nothing is switched on
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);

  delay(2000); // wait for two seconds.

  // LED is switched on
  digitalWrite(2, LOW); //Fairy LED on digital output 2 is switched on
  digitalWrite(3, HIGH);

  delay(2000); // wait for two seconds.

  // the other LED is switched on
  digitalWrite(2, HIGH); 
  digitalWrite(3, LOW); //Fairy LED on digital output 3 is switched on

  delay(2000); // wait for two seconds.

  // both LEDs are switched on
  digitalWrite(2, LOW);  //Fairy LED on digital output 2 is switched on
  digitalWrite(3, LOW); //Fairy LED on digital output 3 is switched on

  delay(2000); // wait for two seconds.


If the program is transferred to the Seeeduino Nano/Arduino, the “lighting show” should look like this:

Video tutorial

SunSpec Tutorial Part I: Modbus

This is a three-part tutorial about SunSpec, an open specification that aims at increasing the interoperability within the solar and distributed energy industry. The goal of this series is to give an overview of the specification and to demonstrate how it can be used for basic use cases. This part focuses on fundamentals about the Modbus protocol which is, at time of writing, the most important communication technology for working with the SunSpec specification. The second part gives a brief introduction to SunSpec in general. It elaborates on the Sunspec information model and its relationship to Modbus. The third part is a hands-on tutorial. It shows how to use an ESP32/ESP8266 to retrieve data from a Kostal Plenticore hybrid inverter (Kostal Plenticore plus).

This introduction should not be seen as a comprehensive guide for starting with Modbus. Instead, only aspects are considered that are important for reading the other two parts and, in addition, might be necessary to get a good understanding of SunSpec in general.


According to the Modbus Application Specification, Modbus is the industry’s serial de facto standard since 1979 and enable millions of automation devices to communicate. Even though technologies originating from the industrial domain are known to have long lifespans, it is kind of remarkable that Modbus has recently been chosen to be SunSpec’s main communication protocol. I came across Modbus several times during my studies, professional or private life. My personal opinion about Modbus is that, on the one hand, appears to be somewhat antiquated, but on the other hand, is also very easy to use. Especially when it comes to simply retrieving data without having too many other requirements, the ease of use of Modbus can be a major advantage.

When talking about Modbus, one has to specify what exactly is meant: Modbus defines the so-called “Modbus Application Layer” that is implemented for various bus/network types (e.g. EIA/TIA-232 or TCP/IP). In particular, the Modbus Application Layer describes a messaging protocol for client/server communication. In further specifications it is describe how to apply the messaging protocol for a specific bus/network type. For example, the application of the Modbus messaging protocol on TCP/IP (Ethernet) is described in “MODBUS Messaging on TCP/IP Implementation Guide V1.0a”.
In this tutorial series, the focus is set to the Modbus messaging protocol over TCP/IP (also called ModbusTCP).

Modbus Protocol (Basics)

Similar to many other protocols, Modbus is a client/server protocol meaning a single or multiple clients communicate with a server. A client is typically a “user application” (e.g. monitoring application), whereas the server is typically a “device” being monitored (e.g. heat pump with sensor values).

The protocol data unit (PDU) of Modbus has two fields: “Function Code” and “Data”. The function code field specifies which action to be performed when a message is sent from the client to the server. The data field contains additional data that belong to the function code. For some function codes / actions, the data field is not required and, therefore, contains 0 bytes.

Besides the PDU, Modbus defines a so-called application data unit (ADU). The ADU contains and additional address, the PDU and an error check. Basically, the ADU is a mapping of a message to a specific bus or network type.

<-                    Application Data Unit (ADU)               ->
| Additional address |  Function code |    Data    | Error Check |
                     <- Protocol Data Unit (PDU ) ->   

Modbus Data Model

Besides the Modbus protocol, there exists a specific data model in Modbus. The data model distinguishes between discrete inputs, coils, input registers and holding registers. Normally, the client send messages (ADUs) to the server in order to retrieve data that is organized based on the Modbus data model. When implementing a Modbus server on a device, the developer can decide how to organize the sensor values into the Modbus data model. The Modbus specification does only restrict the size of the data model elements (1bit or 16bit) and whether a data element is readable or readable/writeable:

Primary tables Object type (size)Type of (r/w)Comments
Discrete InputSingle bitRead-onlyThis type of data can be provided by an I/O system (device).
CoilsSingle bitRead/WriteThis type of data can be alterable by a user application.
Input Registers16-bit wordRead-only This type of data can be provided by an I/O system (device).
Holding Registers16-bit wordRead/Write This type of data can be alterable by a user application.

Accessing data

Let’s assume a device organizes it sensor values according to the Modbus data model. For example, some sensor values are stored as discrete inputs as they are only “true” or “false” (e.g. light barrier sensors) and some other sensor values are input registers as they have to be represented as numeric values (e.g. temperature sensors). Moreover, Let’s assume the device has some configuration values that can also be written by a user application. The device would store them into the coils and holding registers data space.

If a user application (client) wants to access this data, it has to send a message with a function code. For example, if a coil has to be read, function code 0x01 has to be sent to the device (server). In addition to the function code, the client sends also the starting address and the number of coils to be read (stored in the data frame of the message). If successful, the server sends a response with the same function code, the byte count (length of the coil status) and the coil status (actual values).

The following table shows some of the most important functions codes:

Function codeNameAccess
0x01Read CoilsBit access
0x02Read Discrete InputsBit access
0x03Read Holding Registers16 bits access
0x04Read Input Registers16 bits access
0x05Write Single CoilBit access
0x06Write Single Registers16 bits access

Modbus on TCP

When using Modbus on TCP/IP, clients and servers communicate over an Ethernet network. The default port for Modbus servers is 502. It is also possible that servers listen on an additional port, however, according to the specification, port 502 must always be available (“It is important to note that even if another TCP server port is configured for MODBUS service in certain applications, TCP server port 502 must still be available in addition to any application specific ports“).

The Modbus Implementation Guide (MODBUS Messaging on TCP/IP Implementation Guide V1.0a) contains many more details on how to communicate Modbus over TCP/IP. For the two other two parts of this tutorials series, it is not required to know these further details.

In the second part, you will learn more about SunSpec and how it is related to Modbus (coming soon!).

Video Tutorial


WS2813 vs. WS2812B + “WS2813 Mini”-based RGB LED Ring Tutorial

The WS2812B from the WS2812 series has been my favorite RGB chip when it comes to projects where lighting in different colors is involved. Not so long ago, I got notice that there is a successor on the market for already some time. The new RGB chip is called WS2813, which also comes in different variants (e.g. WS2813A, WS2813B, WS2813C,…). In this post, I want to give an introduction into the new features of the new RGB chip and give a short tutorial about WS2813-based RGB rings.

WS2813 Introduction

WS2813B-Mini RGB chip mounted on an LED ring.
WS2813B-Mini RGB chip mounted on an LED ring.

According to the official data sheet of Worldsemi, the first version of the WS2813 was introduced in 2017. At the first look, the WS2813 has the same functionalities as its predecessors: It’s a small chip with an integrated multi-color LED that can light up in different colors (three primary colors with 256 brightness level →  corresponds to 256*256*256= 16777216 colors). Moreover, the RGB chip can be controlled with a single control wire – in addition to the GND- and 5V power signal. The control wire can also be used for cascading multiple RGB chips in order to form an “LED strip” or an “LED ring”.

What are the WS2813’s main features compared to the previous versions (e.g. 2812B)?

  • Dual-signal wires: The control wire is basically doubled by introducing an additional input “backup control data signal input” next to the “control data signal input”. As a result, if a single RGB chip of an LED strip get damaged, the damage does not affect the RGB chips to follow.
  • Increase of refresh frequency: Compared to the WS2812 series, the refresh frequency has been increased from 400 Hz to 2000 Hz.

Fortunately, existing libraries, such as FastLED, Adafruit_NeoPixel, can be used to control also WS2813 RGB chips. Next, I give a tutorial that shows how to control RGB rings.

Tutorial: Controlling WS2813B-Mini RGB Rings with an Arduino Uno

My guess is that most people probably make use of RGB chips in form of LED strips. Alternatively, there are also LED/RGB rings on the market. Recently, I got hand on two variants of Seeedstudio’s RGB LED Ring series which come with WS2813B-Mini RGB chips. In this tutorial, I want to show how to control such an RGB LED Ring with an Arduino Uno.

List of materials:
– Arduino Uno [Search on Aliexpress | Amazon]
– Jumper wires [Search on Aliexpress | Amazon]
– Breadboard [Search on Aliexpress | Amazon]
– RGB LED Ring (my model is Seeedstudio Ultimate RGB LED Ring v1.0)  [Search on Aliexpress | Amazon]

Wiring pin layout of this tutorial.
Wiring pin layout of this tutorial.

Wiring Pin Layout:
The wiring is very simple as only three wires have to be used to connect the Arduino with the LED Ring. I used the “Grove connector” of the LED Ring to make my connections. Grove is a feature available for many components from Seeedstudio. It makes it easier for beginners to connect components, e.g., to an Arduino. Basically, grove users get a shield for their Arduino and then can use standardized connectors to make connections between the components and the shield. Luckily, conventional jumper wires fit very well into the Grove connectors.

Regarding the actual wiring, Arduino’s 5V pin has to be connected to the VCC pin of the LED ring, Arduino’s GND to GND, and Arduino’s digital pin #6 to SIG.

The program represents a very basic lightning demo for the LED ring. The program “walks over” each RGB chip and sets it to a random color. If it has “walked over” all RGB chips, it starts again and overwrites the existing colors with a new random color.

The program is based on the FastLED library which has to be installed on your Arduino IDE installation (Tools -> Manage Libraries…). After including the header file of the FastLED library, the number of available LEDs has to be configured. The actual number depends on your LED ring type. I added a comment to the code about numbers from LED ring types that I’m aware of. In the setup function, FastLED is initialized. Here, you can simply configure it with type WS2813 since the new RGB chip has already been considered by the developers of FastLED.
In each loop function call, an index is incremented. This index represents the “current” RGB chip that is set with a random color. If the index is greater than the number of available RGB chips on the LED ring, it is set back to zero and the whole procedure starts again.

// (c) Michael Schoeffler 2019,

 * This program demonstrates how to use RGB LED Rings. The program "walks over" each LED and sets it to a random color. 
 * If you are already familiar with programming LED stripes, you will recognize that there is basically no difference.
 * The code has been written for the Ultimate RGB LED Ring v1.0, but can easily be adapted. 

#include "FastLED.h"

// some LED numbers that I'm aware of...
// Grove - RGB LED Ring (16-WS2813 Mini): 16 LEDs
// Grove - RGB LED Ring (20-WS2813 Mini): 20 LEDs
// Grove - RGB LED Ring (24-WS2813 Mini): 24 LEDs
// Grove - Ultimate RGB LED Ring: 42 LEDs

#define NUM_LEDS 42 // add number of LEDs of your RGB LED Ring here
#define PIN_LED_RING 6 // digital output PIN that is connected to DIN of the RGB LED Ring
#define UPDATE_TIME 250 // each 250ms, a new random color is set

CRGB rgb_led_ring[NUM_LEDS]; // color array of the LED RGB Ring

unsigned index = 0; // index represents the currently "walked over" LED

void setup() { 
  FastLED.addLeds<WS2813, PIN_LED_RING>(rgb_led_ring, NUM_LEDS); // Change WS2813 to something else, if your RGB LED Ring has a different chipset (e.g. WS2812B instead of WS2813). 

void loop() {
  rgb_led_ring[index] = CHSV(random8(),random8(),random8()); // we set the color of the LED represented by index
  index++; // index is increased, i.e. the color of the LED to the right is set next time.; // state of color array is transfered to RGB LED Ring

  if (index >= NUM_LEDS) { // if we "walked over" all LEDs, we start from the beginning again.
   index = 0; 

  delay(UPDATE_TIME); // program waits for 250ms

Programming variant: Two RGB LED Rings
Some time ago, I made a tutorial about WS2812B-based LED strips. Multiple times, I was asked how to apply the program when having two LED strips. If you make use of FastLED, you can just register a second LED strip (or LED ring) to FastLED in the setup function. I changed the previous program in order to be able to control two LED rings. (Ultimate RGB LED Ring with 42 LEDs and RGB LED Ring “16-WS2813 Mini” with 16 LEDs) . Basically, I just copy’n’pasted each line and made slight changes. Regarding the wiring, I connected the GND of the second ring to a GND pin of the Arduino and then I used a mini breadboard to split up the 5V pin of the Arduino in order to draw 5V connections to both rings. As a last step, I connected the SIG pin to Arduino’s digital pin #7.

// (c) Michael Schoeffler 2019,

 * This program demonstrates how to use two RGB LED Rings at the same time. For each RGB LED Ring, the program "walks over" each LED and sets it to a random color. 
 * If you are already familiar with programming LED stripes, you will recognize that there is basically no difference.
 * The code has been written for the Ultimate RGB LED Ring v1.0, but can easily be adapted. 

#include "FastLED.h"

// some LED numbers that I'm aware of...
// Grove - RGB LED Ring (16-WS2813 Mini): 16 LEDs
// Grove - RGB LED Ring (20-WS2813 Mini): 20 LEDs
// Grove - RGB LED Ring (24-WS2813 Mini): 24 LEDs
// Grove - Ultimate RGB LED Ring: 42 LEDs

#define NUM_LEDS_RING1 42 // add number of LEDs of the first RGB LED Ring here
#define NUM_LEDS_RING2 16 // add number of LEDs of the second LED Ring here

#define PIN_LED_RING1 6 // digital output PIN that is connected to DIN of the first RGB LED Ring
#define PIN_LED_RING2 7 // digital output PIN that is connected to DIN of the second RGB LED Ring

#define UPDATE_TIME 250 // each 250ms, a new random color is set

CRGB rgb_led_ring1[NUM_LEDS_RING1]; // array representing the color layout of the first ring
CRGB rgb_led_ring2[NUM_LEDS_RING2]; // array representing the color layout of the second ring

unsigned index_ring1 = 0; // index represents the currently "walked over" LED of the first ring
unsigned index_ring2 = 0; // index represents the currently "walked over" LED of the second ring

void setup() { 
  FastLED.addLeds<WS2813, PIN_LED_RING1>(rgb_led_ring1, NUM_LEDS_RING1); // Change WS2813 to something else, if the first RGB LED Ring has a different chipset (e.g. WS2812B instead of WS2813). 
  FastLED.addLeds<WS2813, PIN_LED_RING2>(rgb_led_ring2, NUM_LEDS_RING2); // Change WS2813 to something else, if the second RGB LED Ring has a different chipset (e.g. WS2812B instead of WS2813). 

void loop() {
  rgb_led_ring1[index_ring1] = CHSV(random8(),random8(),random8()); // we set the color of the LED represented by index (first ring)
  index_ring1++; // index is increased, i.e. the color of the LED to the right is set next time.

  rgb_led_ring2[index_ring2] = CHSV(random8(),random8(),random8()); // we set the color of the LED represented by index (second ring)
  index_ring2++; // index is increased, i.e. the color of the LED to the right is set next time.;

  if (index_ring1 >= NUM_LEDS_RING1) { // if we "walked over" all LEDs, we start from the beginning again (first ring)
   index_ring1 = 0; 
  if (index_ring2 >= NUM_LEDS_RING2) { // if we "walked over" all LEDs, we start from the beginning again (second ring)
   index_ring2 = 0; 

  delay(UPDATE_TIME); // program waits for 250ms

Video tutorial

Arduino Tutorial: Making the KMR-1.8 SPI (TFT Display) work!

KMR-1.8 SPI TFT display with a resolution of 128 (height) x 160 (width)

Recently, I had the idea to make a digital picture frame—one of these kinds which load images from SD cards and show each image for some time. I was remembering myself that I already own a small TFT display, the KMR-1.8 SPI, that works out of the box with an Arduino Uno. When I digged up my KMR-1.8 SPI, I realized that it has also an in-built SD card reader. Moreover, I looked up the Internet and found ready-to-use libraries for the in-built SD card reader as well as showing images on the TFT display. For these reasons, I thought making such an digital picture frame will turn out very easy.

When I started to implement my first lines of codes and started to connect my Arduino Uno to the KMR-1.8 SPI, I ran into two major problems. First, the colors of my image file did not match to the colors displayed by the KMR-1.8 (red and blue were interchanged). Second, my first prototypes stopped to work after about 5 minutes. The application started to freeze and showed the same image forever instead of displaying the next image after a chosen time. 

I did some research on the Internet and I found out that many people ran into similar problems. The second problem seemed to be caused by some memory leaks in the code. Nevertheless, I did not came across any example code that worked out of the box for my setup. Therefore, I want to share how I made it work. 

List of materials:
– Arduino Uno [Search on Aliexpress | Amazon]
– Jumper wires [Search on Aliexpress | Amazon]
– Breadboard [Search on Aliexpress | Amazon]
– KMR-1.8 SPI TFT display (width 160px, height 128px)  [Search on Aliexpress | Amazon]
– SD Card Adapter [Search on Aliexpress | Amazon]

Important note:

There exists various versions of so-called “1.8 TFT displays” from different manufacturers. Not all of them are 100% compatible to each other. Therefore, if you own a TFT display and want to use my tutorial to make it work, please check if your TFT display really matches the version I used in this tutorial:

Front of my KMR-1.8 TFT display.
Back of my KMR-1.8 TFT display.

Wiring / Pin Layout:

To make it work, you have to connect many wires between the Arduino and the TFT display. I created a fritzing file that shows all the wiring:

The wiring is more or less straight forward. Keep in mind that the Arduino’s 5V pin goes to the VCC pin as well as to the LED+ pin. The same applies to the Arduino’s GND pin which goes to GND and LED- of the TFT display. Arduino’s digital pin #13 goes also to two pins (SCK and SCL). Next, there is a picture of my setup with completed wiring:

Source code:
The source code relies on three header files (and libraries): SPI.h (Link), SD.h (Link) and TFT.h (Link). Please make sure that all of them are correctly installed before trying out my source code (In Arduino IDE: Tools -> Manage Libraries…).

In the introduction of this blog post, I mentioned that I came across two major problems: the colors red and blue were interchanged and my early Arduino programs started to freeze after some time. Luckily, I was able to fix all issues. The following source code works perfect on my setup. My “digital picture frame” does not require to be restarted after some time (my long-term test lasted about two weeks—and no restart was necessary).

I overcame the first problem by not using the default initialization method (“TFTscreen.begin();”) of the TFT library. Instead, I looked up whats inside the “begin”-method. I found a method called “initR” which has a parameter that allows to perform the initialization for a specific chip. Here, the parameter value “INITR_BLACKTAB” worked for me as the colors were then shown correctly. In addition, I call the method “setRotation” with parameter value “1” in order to be conform to the default initialization method. In the end, the code for the setting up the TFT library object looks like this:

// ...
// ...

I solved the second problem (application freezes after some time) by avoiding any possible memory leak, i.e. to “free” every bit of memory that was reserved before as soon as it is not needed anymore. Therefore, you will find a lot of “close”-method calls as well as some weird string handling. When I wrote the code, I thought I could simplify a few things. However, the memory leak problems came back. So, the code might look weird but it works :)

Here is the full source code:

// (c) Michael Schoeffler 2019,

#include <SPI.h>
#include <SD.h>
#include <TFT.h> 

#define PIN_SD_CS 4
#define PIN_TFT_CS 10
#define PIN_DC 9
#define PIN_RST 8

#define DELAY_IMAGE_SWAP 60000 // each image is shown for 60 seconds


void setup() {
  // initialize default serial connection to send debug information
  while (!Serial) {
    // wait until default serial connection is fully set up
  //The following two lines replace "TFTscreen.begin();" => avoids that red and blue (?) are swapped/interchanged
  TFTscreen.background(255, 255, 255); // prints black screen to TFT display
  init_SD(); // function call that initializes SD card

void init_SD() {
  // try to init SD card
  Serial.print(F("SD card init..."));
  if (!SD.begin(PIN_SD_CS)) {
    Serial.println(F("ERROR")); // failed
  Serial.println(F("SUCCESS")); // ok

void loop() {
  File dir ="/"); // open root path on SD card
  File entry;
  char name[16];
  bool worked_once = false;

  while (entry = dir.openNextFile()) { // iteratively opens all files on SD card. 
    Serial.print(F("Opened File: "));
    strcpy(name,; // file name is copied to variable "name"
    entry.close(); // After copying the name, we do not need the entry anymore and, therefore, close it.
    int filename_len = strlen(name);
    if ((filename_len >= 4 && strcmp(name + filename_len - 4, ".BMP") == 0)) { // check if the current filename ends with ".BMP". If so, we might have an image.
      PImage image = TFTscreen.loadImage(name); // loads the file from the SD card
      if (image.isValid()) { // If the loaded image is valid, we can display it on the TFT.
        Serial.println(F("Image is valid... drawing image."));  
        TFTscreen.image(image, 0, 0); // this function call displays the image on the TFT. 
        worked_once = true; // we set this variable to true, in order to indicate that at least one image could be displayed
      } else {
        Serial.println(F("Image is not valid... image is not drawn."));  
      image.close(); // image is closed as we do not need it anymore.
    } else {
      Serial.println(F("Filename does not end with BMP!"));  
  dir.close(); // directory is closed

  if (worked_once == false) { // if not a single image could be shown, we reconnect to the SD card reader.
    Serial.println(F("Warning: Printing an image did not work once! Trying to reinitalize SD card reader."));        


The code looks for image files (*.BMP) on the SD card and shows each image for 60 seconds. You can change the display time by setting “DELAY_IMAGE_SWAP” to a new value.

Important Note: The image files on the SD card must be stored as BMP with a resolution of 160×128 pixels (width x height). Moreover, long file names and special characters must be avoided.

If everything was done correctly, your Arduino-based “digital picture frame” should show the pictures in all their glory. Here is an example picture (tractor-like vehicle that I drew together with my son):

Video tutorial:

How to do statistics with an Arduino? (Arduino -> SD card -> R)

In this tutorial it is shown how to do statistics with data that comes from sensors which are connected to an Arduino. For this purpose, I use the open source statistic program “R”.  Strictly speaking, R is a programming language targeting at statistical computing and graphics. Moreover, it has a huge community that works on tons of packages providing all kinds of algorithms. In order to get the data from an Arduino into an R I make use of an SD card reader/writer. Sensor data (that is captured by the Arduino) is written to an SD card, then read by a desktop PC and loaded into R.

The tutorial explains three aspects: (A) setting up an Arduino (Nano) with sensors and an SD card reader/writer to collect some data, (B) collecting data and transferring it to R, and (C) doing some statistics with the data.

List of materials:
– Arduino Nano [Search on Aliexpress | Amazon]
– Jumper wires [Search on Aliexpress | Amazon]
– Breadboard [Search on Aliexpress | Amazon]
– Ultrasonic sensors [Search on Aliexpress | Amazon]
– MicroSD Card Adapter [Search on Aliexpress | Amazon]
– MicroSD card [Search on Aliexpress | Amazon]
– Helping hand (very optional) [Search on Aliexpress | Amazon]

A (1/2): Setting up an Arduino, some sensors and an SD card reader

Schematic of the tutorial’s setup.

To be honest, the tutorial’s setup of an Arduino and sensors won’t make any real sense. It fulfills only one purpose: to collect some data that can be used to do statistics. I chose to use two ultrasonic sensors of type HC-SR04. In particular, an Arduino is plugged into the center of a breadboard. One ultrasonic sensor is plugged into the left part of the breadboard and the other sensor is plugged into the right part. As a result, the left sensor measures the distance to the left side and the right sensor measures the distance to the right side. I won’t cover the wiring in detail since I already wrote a tutorial that shows how to connect an HC-SR04 to an Arduino. In addition, I wire an SD card read/writer to the Arduino. Again, I won’t go into details here as I also wrote a tutorial about wiring an SD card reader to the Aruino.

Setup of the tutorial. The ultrasonic sensors measure the distance to the left and right side. The collected data is used to do some statistics.

Setup of the tutorial. The ultrasonic sensors measure the distance to the left and right side. The collected data is used to do some statistics.

In my previous tutorial about the HC-SR04 ultrasonic sensor, I made use of an approach that uses the pulseIn-function. This approach works well if you have only a single ultrasonic sensor in your setup. If you have two ultrasonic sensors that measure the distance at the same time, then this approach does not work anymore. In this tutorial, I make use of interrupts. These interrupts are triggered when the signal on the corresponding pin changes its state. On the Arduino Uno and Arduino Nano only digital pin 2 and 3 can be bound to interrupts. Therefore, the echo pins of the ultrasonic sensors are connected to digital pin 2 and 3.

A (2/2): Programming

As just mentioned, interrupts are used to measure the distances. Besides that, everything else is similar to the code that was presented in my previous tutorials about the HC-SR04 and Micro SD card reader.
On each loop function call, the distances are measured and then written to the file. Moreover, the old file is deleted on each startup of the Arduino.

// (c) Michael Schoeffler 2018,
#include <SD.h> //Load SD library

// Pins of the ultrasonic sensors
const int pin_echo_left = 2;
const int pin_echo_right = 3;
const int pin_trig_left = 6;
const int pin_trig_right = 7;

// variables that to track the duration of the echo
volatile long echo_left_start = 0;
volatile long echo_left_end = 0;
int distance_left = 0;

volatile long echo_right_start = 0;
volatile long echo_right_end = 0;
int distance_right = 0;

// variables to write data to sd card
int chipSelect = 4; //chip select pin for the MicroSD Card Adapter
File file; // file object that is used to write the data

void setup() {
  pinMode(pin_trig_left, OUTPUT);
  pinMode(pin_echo_left, INPUT);

  pinMode(pin_trig_right, OUTPUT);
  pinMode(pin_echo_right, INPUT);

  attachInterrupt(digitalPinToInterrupt(pin_echo_left), echo_interrupt_left, CHANGE); // only pins 2 and 3 are useable for interrupts on Uno, Nano and Mini
  attachInterrupt(digitalPinToInterrupt(pin_echo_right), echo_interrupt_right, CHANGE);

  pinMode(chipSelect, OUTPUT); 
  if (!SD.begin(chipSelect)) { // Initialize SD card
    Serial.println("Could not initialize SD card."); // if return value is false, something went wrong.
  if (SD.exists("stats.csv")) { // if "stats.csv" exists, fill will be deleted in order to gather new data
    Serial.println("File exists.");
    if (SD.remove("stats.csv") == true) {
      Serial.println("Successfully removed file.");
    } else {
      Serial.println("Could not removed file.");

  // write headers
  file ="stats.csv", FILE_WRITE); // open "file.csv" to write data
  if (file) { // Next, the headers (first line) of the CSV file is written
      file.close(); // close file
      Serial.println("Headers were written to file!");
  } else {
    Serial.println("Could not open file (writing).");


// this function is called by an interrupt on each change of the echo pin of the left sensor
void echo_interrupt_left() {
  switch (digitalRead(pin_echo_left))
  case HIGH:
    echo_left_end = 0;
    echo_left_start = micros();
  case LOW:
    if (echo_left_end == 0) {
      echo_left_end = micros();
      long duration = echo_left_end - echo_left_start;
      long durationOneWay = duration / 2; // divided by two, since duration is a roundtrip signal
      // acoustic velocity of air at a temperature of 20°C => ~343.5 m/s
      // => 0.03435 cm/us
      distance_left = durationOneWay * 0.03435; // distance in cm

// this function is called by an interrupt on each change of the echo pin of the right sensor
void echo_interrupt_right() {
  switch (digitalRead(pin_echo_right))
  case HIGH:
    echo_right_end = 0;
    echo_right_start = micros();
  case LOW:
    if (echo_right_end == 0) {
      echo_right_end = micros();
      long duration = echo_right_end - echo_right_start;
      long durationOneWay = duration / 2; // divided by two, since duration is a roundtrip signal
      // acoustic velocity of air at a temperature of 20°C => ~343.5 m/s
      // => 0.03435 cm/us
      distance_right = durationOneWay * 0.03435; // distance in cm

void loop() {
    // both ultrasonic are triggered to send an ultrasonic signal
    digitalWrite(pin_trig_left, LOW); // turn off the trigger
    digitalWrite(pin_trig_right, LOW); // turn off the trigger
    digitalWrite(pin_trig_left, HIGH);// prepare to send "trigger" command to module
    digitalWrite(pin_trig_right, HIGH);// prepare to send "trigger" command to module
    delayMicroseconds(10); // wait for 10us (module sends signal only, if trigger had a HIGH signal for at least 10 us)
    digitalWrite(pin_trig_left, LOW); // module sends signal now
    digitalWrite(pin_trig_right, LOW); // module sends signal now

    delay(1000); // we wait a second... interrupts should be called by now

    // next we append the measurements to the CSV file.
    file ="stats.csv", FILE_WRITE); // open "file to write data
    if (file) {
      file.close(); // close file
      Serial.println("Values were written to file!");
      Serial.print("Left:  ");
      Serial.print("Right: ");
    } else {
      Serial.println("Could not open file (writing).");

B: Collecting data and transferring it to R

Arduino is moved to the left and to the right in order to collect some data.

Arduino is moved to the left and to the right in order to collect some data.

Next, some data is collected by simply switching on the Arduino and then moving it to the left and to the right for a little while. After some time, the stats.csv file on the SD card should have enough distance values. In particular, the Arduino program stores the distances measurements in a so-called Comma Separated Value (CSV) format. The first line of the stats.csv file represents the so-called header. The header contains the labels for the data. The next lines contain the actual measurements. Most statistics programs offer a functionality to import CSV files. Moreover, since CSV file can be found with slightly different formatting, statistic programs usually have some additional convenience functions to import a CSV file. For example, some CSVs have headers and others don’t have them. Furthermore, some CSV files separate the data entries by ‘comma’, others separate by ‘semicolon’.

C: Doing some statistics

So the next step is to plug the SD card from the Arduino’s SD card reader to a desktop PC. Then, the data from the SD card can be loaded to R. There exist many different graphical user interfaces for R. Usually, I use RStudio which makes R accessible especially for beginners. I won’t cover how to install RStudio. You can find more information about the installation on the RStudio website. Keep in mind that RStudio is only a user interface for R. Besides RStudio, you have also to install R itself. Moreover, this tutorial does also not cover an introduction to R. If you are not familiar with R, then you might want to take a look at an R tutorial for beginners in order to fully understand this tutorial.

RStudio workspace. In RStudio it is very easy to set the working directory.

If you have installed R (and a graphical user interface), start with loading the file “stats.csv” to your R environment. In R, the environment is a place to store variables. In this tutorial, I set the working directory of R to the folder which contains the “stats.csv”. Typically, user interfaces such as RStudio offer a option for setting the working directory. In order to store the CSV file to the R environment, you have to enter data = read.csv("STATS.CSV"); to the R console. As a result, you should see that a variable “data” has been added to your environment. This variable should contain two variables and some observations for both variables. The number of observation is dependent on how long you took distance measurements with the setup. If you did not set the working directory, then you have to enter the full path to the CSV file into the “read.csv”-function.

Next, we want to calculate the mean values of our distance measurements. This can be done by entering mean(data$DISTANCE_LEFT)and mean(data$DISTANCE_RIGHT)to the R console. For the left distance I get 12.13043 and for the right distance I get 13.34783 [cm].

If you know only my mean values, you don’t know whether I actually moved my Arduino or not. I would be also possible that I just placed my Arduino in between the obstacles having a distance of about 12cm to the left and about 13cm to the right. In statistics, you can make use of the standard deviation which gives you some indication about the amount of variations of the observations. More strictly speaking, the standard deviation is the square root of the variance. Maybe you are asking yourself now “What is the variance?”. The variance is the average of the squared differences between each observation and the mean.  In R, the standard deviations of the left and right measurements are calculated by entering sd(data$DISTANCE_LEFT) and sd(data$DISTANCE_RIGHT). The result values are 3.876554 (left) and 7.036326 (right). Normally, one would expect that the standard deviants are about the same for the left and right distance measurements. The differences are a result of a non-symmetrical physical setup, measurement noise, skewed movements of the Arduino etc. In order to check whether both variables are related to each other, a correlation measure can be utilized. The Pearson product-moment correlation coefficient, denoted as r, is such a measure. This coefficient ranges from -1 to 1. A value of -1 indicates that the two variables perfectly disagree, a value of 0 indicates that the two variables are independent, and a value of 1 indicates that the two variables perfectly agree. In R, the Pearson product-moment correlation coefficient can be calculated by entering cor(data$DISTANCE_LEFT, data$DISTANCE_RIGHT). As you can see, the function has two arguments which correspond to the two variables of interest. Applying the function to my data returns 0.7782926. This indicates that the two variables rather disagree. This sound logical because if we move the Arduino to the left, the left distance measurement decreases. At the same time, the right distance measurement increases. If both distance measurements had increased at the same time, the correlation coefficient would be close to +1.

While moving the Arduino, an obstacle is placed next to the right distance sensor. As a result, the correlation between the sensor data is very low.

Let’s modify our setup. This time, I place an obstacle (mini breadboard) next to the right sensor while moving the Arduino (see picture). Then, I repeat the whole process to get the new data into R.  Instead of storing the data values into the variable “data”, I create a new variable “data2”. The function call for reading the CSV file looks like this: data2 = read.csv("STATS.CSV");.
Next you can find the result values for calculating the mean values, standard deviations as well as the correlation coefficient:
mean(data2$DISTANCE_LEFT)= 11.94737
mean(data2$DISTANCE_RIGHT)= 3.421053
sd(data2$DISTANCE_LEFT)= 3.822066
sd(data2$DISTANCE_RIGHT)= 0.606977
cor(data2$DISTANCE_LEFT, data2$DISTANCE_RIGHT)= -0.2054429

From looking at the statistics results alone, one might guess that something happened to the right sensor. For example, the standard deviation is very low (0.6…). If an obstacle is in front of the sensor, it will measure – more or less – the same distance value. As a result, the standard deviation is close to zero. Moreover, as one sensor measures the actual (varying) distance and the other sensor always measures the same distance, both sensor values are not correlated to each other any more. As a result, the correlation coefficient is close to zero.

You have seen that you can use statistics “to measure what is going on in the real world”. Moreover, at least in my opinion, statistics can be as useful as, for example, a multimeter when it comes to finding malfunctions of a system or bugs in you code.

Video tutorial:

Arduino-Tutorial: How to use the RDM630/RDM6300 RFID reader

On the left hand side is the RM6300, which is a very affordable (1-3$) RFID reader. On the right hand side, is the RDM630. The RDM630 is more expensive (10-15$) but also more robust.

On the left hand side is the RDM6300, which is a very affordable (1-3$) RFID reader. On the right hand side, is the RDM630. The RDM630 is more expensive (10-15$) but also more robust.

Seeedstudio’s RDM630 and the RDM6300 are two different types of RFID readers, even though they often get mixed up. Besides the similar names, another reason for the confusion might be that they share the same pin layout and transfer protocol. According to my knowledge, besides the same technical functionality, they have nothing else in common (like same manufacturer, predecessor/successor version, etc.). Usually, you should be able to purchase Seeedstudio’s RDM630 for about 10-15$ and the RDM6300 for about 1-3$. In my opinion, the price difference can be easily justified as the RDM630 is more robust and allows much higher reading distances.

Both readers work at a frequency of 125 kHz and enable to read EM4100-compatible tags. Other tags (for example EM4305 tags) do not (or might not) work with these readers.

In this tutorial, I demonstrate how to use the RDM6300 and the RDM630 with an Arduino Uno. Luckily, both RFID readers use the same protocol via a serial connection. As a result, the same source code can be used to make both readers work. Therefore, regardless of whether you own an RDM630 or RDM6300, this tutorial should be of use to you if you want to make it work with an Arduino.

List of materials:
– Arduino Uno  [Search on Aliexpress | Amazon]
– Jumper wires  [Search on Aliexpress | Amazon]
– Seeedstudio RDM630* [Search on Aliexpress | Amazon]
– RDM6300 [Search on Aliexpress | Amazon]
– 125 kHz tags (check for EM4100 compatibility) [Search on Aliexpress | Amazon]
* If you are looking for an RDM630, please keep in mind that many offers are wrongly labeled. You can find many products that are advertised as “RDM630”, although they are of type RDM6300. Therefore, take a close look at the pictures.

Pin layout RDM630 / RDM6300:

Pin layout of the RDM630. The RDM6300 has the exact same pin layout.

Pin layout of the RDM630. The RDM6300 has the exact same pin layout.

In total, the RDM630/RDM6300 has 9 pins, where Vcc and GND exist two times. The Vcc pin must be connected to a 5V DC. ANT1 and ANT2 are used to connect an antenna to the board. Typically, the RDM630 as well as the RDM6300 already come with an antenna. Nonetheless, it is possible to switch the antenna, for example to a custom-made antenna. TX is used to transmit data and RX is used to retrieve data. The LED pin can be used to quickly lookup whether an RFID tag was successfully read. If RFID tag is not present, the LED pin is at 5V (HIGH). If an RFID was read, the LED pin goes to 0V (LOW) for some moments (yes, it goes from HIGH to LOW!).

Wiring to Arduino Uno:

Wiring between an Arduino Uno and a RDM6300. The same wiring can be applied to an RDM630.

Wiring between an Arduino Uno and a RDM6300. The same wiring can be applied to an RDM630.

In this tutorial, four pins of the RDM630/RDM6300 are wired to the Arduino Uno. Vcc has to be connected to the Arduino’s 5V pin (red wire) and GND to the Arduino’s GND (black wire). The TX pin has to be connected to digital pin #6 (green wire). Basically, the RX pin is not required as we do not send data to the RFID module in this tutorial. For the sake of completeness, RX is connected to digital pin #8 (yellow wire). Lastly, the antenna is connected to ANT1 and ANT2 (polarity does not matter).

Example source code:
As mentioned before, the same source code can be used with both models (I tested the code with both models). Basically, when an RFID tag was detected the RDM630/RDM6300 sends a frame with 14 bytes:  head [1 byte], data [10 byte],  checksum [2 byte], and tail [1 byte]. The head (or preamble) is always 0x02. Similarly, the tail is always 0x03. The data field contains ASCII-encoded HEX values. Sometimes, manufacturers tend to split the RFID tag data field into two parts: version and tag. Therefore, depending on your RFID tag type, the first two bytes of data might be the version and the other 8 bytes the actual RFID tag. I considered this in my source code. In order to calculate the checksum from the data field, one has to perform an XOR-operation over all data entries.

// (c) Michael Schoeffler 2018,
#include <SoftwareSerial.h>

const int BUFFER_SIZE = 14; // RFID DATA FRAME FORMAT: 1byte head (value: 2), 10byte data (2byte version + 8byte tag), 2byte checksum, 1byte tail (value: 3)
const int DATA_SIZE = 10; // 10byte data (2byte version + 8byte tag)
const int DATA_VERSION_SIZE = 2; // 2byte version (actual meaning of these two bytes may vary)
const int DATA_TAG_SIZE = 8; // 8byte tag
const int CHECKSUM_SIZE = 2; // 2byte checksum

SoftwareSerial ssrfid = SoftwareSerial(6,8); 

uint8_t buffer[BUFFER_SIZE]; // used to store an incoming data frame 
int buffer_index = 0;

void setup() {
 Serial.println("INIT DONE");

void loop() {
  if (ssrfid.available() > 0){
    bool call_extract_tag = false;
    int ssvalue =; // read 
    if (ssvalue == -1) { // no data was read

    if (ssvalue == 2) { // RDM630/RDM6300 found a tag => tag incoming 
      buffer_index = 0;
    } else if (ssvalue == 3) { // tag has been fully transmitted       
      call_extract_tag = true; // extract tag at the end of the function call

    if (buffer_index >= BUFFER_SIZE) { // checking for a buffer overflow (It's very unlikely that an buffer overflow comes up!)
      Serial.println("Error: Buffer overflow detected!");
    buffer[buffer_index++] = ssvalue; // everything is alright => copy current value to buffer

    if (call_extract_tag == true) {
      if (buffer_index == BUFFER_SIZE) {
        unsigned tag = extract_tag();
      } else { // something is wrong... start again looking for preamble (value: 2)
        buffer_index = 0;

unsigned extract_tag() {
    uint8_t msg_head = buffer[0];
    uint8_t *msg_data = buffer + 1; // 10 byte => data contains 2byte version + 8byte tag
    uint8_t *msg_data_version = msg_data;
    uint8_t *msg_data_tag = msg_data + 2;
    uint8_t *msg_checksum = buffer + 11; // 2 byte
    uint8_t msg_tail = buffer[13];

    // print message that was sent from RDM630/RDM6300

    Serial.print("Message-Head: ");

    Serial.println("Message-Data (HEX): ");
    for (int i = 0; i < DATA_VERSION_SIZE; ++i) {
    Serial.println(" (version)");
    for (int i = 0; i < DATA_TAG_SIZE; ++i) {
    Serial.println(" (tag)");

    Serial.print("Message-Checksum (HEX): ");
    for (int i = 0; i < CHECKSUM_SIZE; ++i) {

    Serial.print("Message-Tail: ");


    long tag = hexstr_to_value(msg_data_tag, DATA_TAG_SIZE);
    Serial.print("Extracted Tag: ");

    long checksum = 0;
    for (int i = 0; i < DATA_SIZE; i+= CHECKSUM_SIZE) {
      long val = hexstr_to_value(msg_data + i, CHECKSUM_SIZE);
      checksum ^= val;
    Serial.print("Extracted Checksum (HEX): ");
    Serial.print(checksum, HEX);
    if (checksum == hexstr_to_value(msg_checksum, CHECKSUM_SIZE)) { // compare calculated checksum to retrieved checksum
      Serial.print(" (OK)"); // calculated checksum corresponds to transmitted checksum!
    } else {
      Serial.print(" (NOT OK)"); // checksums do not match


    return tag;

long hexstr_to_value(char *str, unsigned int length) { // converts a hexadecimal value (encoded as ASCII string) to a numeric value
  char* copy = malloc((sizeof(char) * length) + 1); 
  memcpy(copy, str, sizeof(char) * length);
  copy[length] = '\0'; 
  // the variable "copy" is a copy of the parameter "str". "copy" has an additional '\0' element to make sure that "str" is null-terminated.
  long value = strtol(copy, NULL, 16);  // strtol converts a null-terminated string to a long value
  free(copy); // clean up 
  return value;

If you execute the source code and hold an RFID tag close to the antenna, the Arduino IDE’s serial monitor should output something like this:

Video tutorial: