Authors

IOT-OPEN.EU Reloaded Consortium partners proudly present the 2nd edition of the Introduction to the IoT book. The complete list of contributors is juxtaposed below.

ITT Group

  • Raivo Sell, Ph. D., ING-PAED IGIP
  • Rim Puks, Eng.
  • Mallor Kingsepp, Eng.

Riga Technical University

  • Agris Nikitenko, Ph. D., Eng.
  • Karlis Berkolds, M. sc., Eng.
  • Anete Vagale, M. sc., Eng.
  • Rudolfs Rumba, M. sc., Eng.

Silesian University of Technology

  • Piotr Czekalski, Ph. D., Eng.
  • Krzysztof Tokarz, Ph. D., Eng.
  • Oleg Antemijczuk, M. sc., Eng.
  • Jarosław Paduch, M. sc., Eng.
  • Godlove Suila Kuaban, M. sc., Eng.

Tallinn University of Technology

  • Raivo Sell, Ph. D., ING-PAED IGIP
  • Karl Läll, B. sc., Eng.

SIA RobotNest

  • Karlis Berkolds, M. sc., Eng.

IT Silesia

  • Łukasz Lipka, M. sc., Eng.

University of Messina

  • Salvatore Distefano
  • Rustem Dautov
  • Riccardo Di Pietro
  • Antonino Longo Minnolo

ITMO University

  • Aleksandr Kapitonov, Ph. D., Assoc. Prof.
  • Dmitrii Dobriborsci, M. sc., Eng.
  • Igor Pantiukhin, M. sc., Eng.
  • Valerii Chernov, Eng.

Graphic Design and Images

  • Blanka Czekalska, M. sc., Eng., Arch.
  • Piotr Czekalski, Ph. D., Eng.

Reviewers (1st edition)

  • Fabio Bonsignorio, Ph. D., Eng.– Professor at Scuola Superiore Sant'Anna, Institute of Biorobotics
  • Artur Pollak, M. sc., Eng. – CEO at APAGroup
  • Ivars Parkovs, M. sc., Eng. – R&D Senior Engineer at “SAF Tehnika” Ltd.
  • Janis Lacaunieks, M. sc., Eng. – R&D Engineer at “SAF Tehnika” Ltd.

Versions

This page keeps track of the content reviews and versions done as a continuous maintenance process

Table 1: Versions and Content Updates
Version Update Date Content updates summary Other comments
1 v 0.1 01.05.2023 Preliminary version
2 v 0.2 02.05.2023 Authors, Preface and Versions Authors to be updated with consortium partners
3 v 0.25 08.05.2023 Authors, Preface, Project Information Authors to be updated with consortium partners
4 v 0.26 18.06.2023 Introduction chapter revised
5 v 0.27 28.06.2023 Updated contents of the Mobility (Fog, edge), enabling technologies, IoT definition
6 v 0.28 28.06.2023 Introduction to the Embedded Programming updated: exposed edge class devices and modified Scripting section for Fog devices. Introduction to the Programming Frameworks updatd to expose Edge class and refer to fog.
7 v 0.29 28.06.2023 Updated Analogue section in embeddedcommunicationprotocols
8 v.0.3 19.08.2023 Reviewed and revised Actuators
9 v.0.31 20.08.2023 Updated hardware specific extensions for C++ programming
10 v.0.32 21.08.2023 Implemented PWM

Preface

This book and its offshoots were prepared to provide comprehensive information about the Internet of Things on the engineering level.
Its goal is to introduce IoT to bachelor students, master students, technology enthusiasts and engineers willing to extend their current knowledge with the latest hardware and software achievements in the scope of the Internet of Things.
This book is also designated for teachers and educators willing to prepare a course on IoT.

We (Authors) assume that persons willing to study this content possess some general knowledge about IT technology, i.e. understand what an embedded system is, know the general idea of programming (in C/C++) and are aware of wired and wireless networking as it exists nowadays.

This book constitutes a comprehensive manual for IoT technology; however, it is not a complete encyclopedia nor exhausts the market. The reason for it is pretty simple – IoT is so rapidly changing technology that new devices, ideas and implementations appear daily. Once you read this book, you can quickly move over the IoT environment and market, easily chasing ideas and implementing your IoT infrastructure.

We also believe this book will help adults that took their technical education some time ago to update their knowledge.

We hope this book will let you find brilliant ideas in your professional life, see a new hobby, or even start an innovative business.

Playing with real or virtual hardware and software is always fun, so keep going!

Project Information

ThisBook was implemented under the Erasmus+ KA2 projects:

  • Strategic Partnerships in the Field of Education, Training, and Youth – Higher Education, 2016, IOT-OPEN.EU – Innovative Open Education on IoT: Improving Higher Education for European Digital Global Competitiveness, project number: 2016-1-PL01-KA203-026471,
  • Cooperation Partnerships in higher education, 2022, IOT-OPEN.EU Reloaded: Education-based strengthening of the European universities, companies and labour force in the global IoT market, project number: 2022-1-PL01-KA220-HED-000085090.

Erasmus+ Disclaimer
This project has been funded with support from the European Commission.
This publication reflects the views only of the author, and the Commission cannot be held responsible for any use which may be made of the information contained therein.

Copyright Notice
This content was created by the IOT-OPEN.EU Consortium: 2016–2019 and IOT-OPEN.EU Reloaded Consortium 2022-2025.
The content is Copyrighted and distributed under CC BY-NC Creative Commons Licence, free for Non-Commercial use.

CC BY-NC

In case of commercial use, please contact IOT-OPEN.EU Reloaded Consortium representative.

Introduction

Here comes the Internet of Things. The name that recently makes red-hot people in business, researchers, developers, geeks and … students. The name that non-technology related people consider a kind of magic and even a danger to their privacy. The EU set the name as one of the emerging technologies and estimated the worldwide market will hit well over 500 billion US dollars in 2022, while the number of IoT devices in 2030 is expected to be over 3.2 billion.

What is IoT (Internet of Things), then? Surprisingly, the answer is not straightforward.

Color coding

To simplify the selection of different topics, a simple colour coding was introduced, indicating the skills required to cover particular topics. Colour codes are organized in the form of colour bars enclosing chapter titles.

Explanation:

This chapter is for beginners (including amateurs) or higher

This chapter is for bachelor students of higher

This chapter is for master students of higher

Definition of IoT

Let us roll back to the 1970s first. In 1973 the first RFID device was patented. This device was the key enabling technology even if it does not look nor remind modern IoT devices. The low power (actually here passive) solution with a remote antenna large enough to collect energy from the electromagnetic field and power the device brought an idea of uniquely identifiable items. That somehow mimics well-known EAN barcodes and the evolution used nowadays, like QR codes, but every single thing has a different identity here. In contrast, EAN barcodes present a class of products, not an individual one. The possibility to identify a unique identity remotely became fundamental to the IoT as it's known today. RFID is not the only technology standing behind IoT. In the 1990s, the rapid expansion of wireless networks, including broadband solutions like cellular-based data transfers with their consequent generations, enabled connecting devices in various, even distant geographical locations. Parallelly an exponential increase in the number of devices connected to the global Internet network was observed, including the smartphone revolution that started around mid the first decade of the XXI century. On the hardware level, microchips and processors became physically smaller and more energy efficient yet offering growing computing capabilities and memory size increase, along with significant price drops. All those facts drove the appearance of small, network-oriented, cheap and energy-efficient electronic devices. In recent years, the development of efficient AI technologies even boosted IoT applications.

What is IoT?

The phrase “Internet of Things” was used for the first time in 1999 by Kevin Ashton – an expert on digital innovation. Formally IoT was introduced by the International Telecommunication Union (ITU) in the ITU Internet report in 2005 [1]. The understanding and definitions of IoT changed over the years, but now all agree that this cannot be seen as a technology issue only. According to IEEE “Special Report: Internet of Things” [2] released in 2014, IoT is:

IEEE Definition of IoT
A network of items – each embedded with sensors – connected to the Internet.

It relates to the physical aspects of IoT only. The Internet of Things also addresses other aspects that cover many areas [3]:

  • enabling technologies,
  • software,
  • applications and services,
  • business models,
  • social impact,
  • security and privacy aspects.

IEEE, as one of the most prominent standardisation organisations, also works on standards related to the IoT. The primary document is IEEE P2413™ [4]. It covers the technological architecture of IoT as three-layered: sensing at the bottom, networking and data communication in the middle, and applications on the top. It is essential to understand that IoT systems are not only small, local-range systems. ITU-T has defined IoT as:

ITU-T Definition of IoT
A global infrastructure for the information society, enabling advanced services by interconnecting (physical and virtual) things based on existing and evolving interoperable information and communication technologies.

In the book [5] by European Commission, we can read a similar description of what IoT is: “The IoT is the network of physical objects that contain embedded technology to communicate and sense or interact with their internal states or the external environment.” IoT impacts many areas of human activity: manufacturing, transportation, logistics, healthcare, home automation, media, energy saving, environment protection and many more. In this course, we will consider the technical aspects mainly.

"Thing"

In the IoT world, the “thing” is always equipped with some electronic element that can be as simple as the RFID tag, an active sensor sending data to the global network, or an autonomous device that can react to environmental changes. In CERP-IoT book “Visions and Challenges” [6] in the context of “Internet of Things” a “thing” could be defined as:

CERP-IoT Definition of “Thing”
A real/physical or digital/virtual entity that exists and moves in space and time and can be identified. Assigned identification numbers, names and location addresses commonly identify things.

It is quite easy to find other terms used in the literature like “smart object”, “device”, or “nodes” [7].

Passive Thing

One can imagine that almost everything in our surroundings is tagged with an RFID element. They do not need a power supply; they respond with a short message, usually containing the identification number. Modern RFID can achieve 6 to 7 meters of the range. Using the active RFID reader, we can quickly locate lost keys and know if we still have the butter in the fridge and in which wardrobe there is our favourite t-shirt.

Active Thing

If the “thing” includes the sensor, it can send interesting data about current conditions. We can sense environmental parameters like temperature, humidity, air pollution, pressure, localisation data, water level, light, noise, and movement. Using different methods and protocols, this data can be sent to the central collector that connects to the Internet and the database or cloud. There the data can be processed, and Artificial Intelligence algorithms can be used to decide actions that could be taken in different situations. Active things can also receive control signals from the central controller to control the environment: turn on/off the heating or light, water flowers, and turn on the washing machine when there is enough sunlight to generate the required electricity or charge your electric car.

Autonomous Thing

This thing does not even require the controller to realise the proper decision. An autonomous vacuum cleaner can clean our house when it detects that we aren't home and the floor needs cleaning. The fridge can order our favourite beverage once the last bottle is almost empty.

Sensor Network

Sensor Networks are a subset of the IoT devices used as a collaborative solution to grab data and send it for further processing. Opposite to the general IoT devices, Sensor Network devices do not have any actuators that can apply an action to the external world. The data flow is unidirectional, then.

IoT vs Embedded Systems

IoT systems and embedded systems share almost the same domain. They frequently use the same microcontrollers, sensors and actuators, development software and even programming models. What differs between IoT and embedded systems is that IoT, on its principles, uses communication to send and receive data outside of its instance, while embedded systems do not have to. Embedded systems do not have to be network-enabled, and they do not have a unique identity frequently, while IoT devices do. Moreover, IoT systems are complex and multilayered, often introducing cloud-based parts, while embedded systems are stand-alone devices. Shortly we can say that an IoT device is network enabled embedded system.

Enabling Technologies

In this chapter, there is an approach to describe modern technologies that appeared in the last few years, enabling the idea of IoT to be widely implementable. In the [8] one can read that “The confluence of efficient wireless protocols, improved sensors, cheaper processors and a wave of startups and established companies made the concept of the IoT mainstream”. Similar analysis has been done in [9] where authors write that “the latest developments in RFID, smart sensors, communication technologies and Internet protocols enable the IoT”. RFID and smart sensors need the microprocessor system to read, convert the data into digital format, and send it to the Internet using the communication protocol. This process can be done by small- and medium-scale computer (embedded) systems. These are essential elements of technologies used in IoT systems.

Edge class devices

In recent years one can observe rapid growth in the field of microprocessors. It includes not only the powerful desktop processors but also microcontrollers – elements that are used in small-scale embedded systems. We can also notice the popularity of microprocessor systems that can be easily integrated with other factors, like sensors, and actuators, connected to the network. Essential is also the availability of programming tools and environments supported by different companies and communities. An excellent example of such a system is Arduino. Those devices are low-power, constrained devices, usually battery-powered and, in most cases, communicating wirelessly.

Fog class devices

The same growth can be observed in the advanced constructions comparable to low-end computers. They have more powerful processors, memory and networking connectivity built-in than small-scale computer systems. They can work under the control of multitasking operating systems like Linux and Windows and embedded or real-time operating systems like FreeRTOS. Having many libraries, they can successfully work as hubs for local storage, local controllers and gateways to the Internet. Raspberry Pi and the nVidia Jetson series are examples of such systems. This category of devices frequently contains hardware accelerated (such as GPU) AI-capable solutions, i.e. nVidia Jetson Nano or Xavier series. Those devices can be battery or mains powered. Often, they are green energy powered: i.e. with a larger backup battery and energy harvesting solution (such as solar panel).

Access to the Internet

Nowadays, the Internet is (almost) everywhere. There are lots of wireless networks available in private and public places. The price of cellular access (3G/4G/5G) is low, offering a suitable data transfer performance. Connecting the “thing” to the Internet has never been so easy.

IP Addressing Evolution

The primary paradigm of IoT is that every unit can be individually addressed. With the addressing scheme used in IPv4, it wouldn't be possible. IPv4 address space delivers “only” 4 294 967 296 of unique addresses (2^32). If you think it's a considerable number, imagine that every person in the world has one IP-connected device – IPv4 covers about half of the human population. The answer is IPv6 with a 128-bit addressing scheme that gives 3.4 × 10^38 addresses. It will be enough even if everyone has a billion devices connected to the Internet.

Data Storage and Processing

IoT devices generate the data to be stored and processed somewhere. If there is a couple of sensors, the amount of data is not very big, but if there are thousands of sensors generating data hundreds of times every second. The cloud can handle it – the massive place for the data with tools and applications ready to help with data processing. Some big, global clouds are available for rent, offering not only storage but also Business Intelligence tools, Artificial Intelligence analytic algorithms. There are also smaller private clouds created to cover the needs of one company only. Many universities have their own High-Performance Computing Centre.

Mobile Devices

Many people want to be connected to the global network everywhere, anytime, having their “digital twin” with them. It is possible now with small, powerful mobile devices like smartphones. Smartphones are also elements of the IoT world, being together sensors, user interfaces, data collectors, wireless gateways to the Internet, and everything with mobility features.

The technologies we mentioned here are the most recognisable. Still, there are many others, more minor, described only in the technical language in some standard description document, hidden under the colourful displays between large data centres, making our IoT world operable. In this book, we will describe some of them.

A special note on Fog class and Edge class devices

Technology development instantly shifts devices between categories. A border between Fog and Edge class devices is conventional; many can share both worlds. It depends on their purpose, application and performance configuration; thus, i.e. Raspberry Pi can be an end-node (Edge) class device and a Fog class, working as a data aggregator and analytical device.

Mobility – New Paradigm for IoT Systems

IoT has already been defined as a network of physical things or devices that might include sensors or simple data processing units, complex actuators, and significant hybrid computing power. Today IoT systems have transitioned from being perceived as sensor networks to smart-networked systems capable of solving complex tasks in mass production, public safety, logistics, medicine and other domains, requiring a broader understanding and acceptance of current technological advancements, including advanced data processing that includes AI.

Since the very beginning of sensor networks, one of the main challenges has been data transport and data processing, where significant efforts have been put by the ICT community towards service-based system architectures. However, The current trend already provides considerable computing power even in small mobile devices. Therefore, the concepts of future IoT already shifted towards smarter and more accessible IoT devices, and data processing has become possible closer to the Fog and Edge.

Cloud Computing

Cloud-based computing is a relatively well-known and adequately employed paradigm where IoT devices can interact with remotely shared resources such as data storage, data processing, data mining and other services are unavailable to them locally because of the constrained hardware resources (CPU, ROM, RAM) or energy consumption limits. Although the cloud computing paradigm can handle vast amounts of data from IoT clusters, the transfer of extensive data to and from cloud computers presents a challenge due to limited bandwidth[10]. Consequently, there is a need to process data near data sources, employing the increasing number of smart devices with enormous processing power and a rising number of service providers available for IoT systems.

Fog Computing

Fog computing addressed the bottlenecks of cloud computing regarding data transport while providing the needed services to IoT systems. It is a new trend in computing that aims to process the data near the data source. Fog computing pushes applications, services, data, computing power, and decision-making away from the centralised nodes to the logical extremes of a network. Fog computing significantly decreases the data volume that must be moved between end devices and the cloud. Fog computing enables data analytics and knowledge generation at the data source. Furthermore, the dense geographic distribution of fog helps to attain a better-localised accuracy for many applications than the cloud processing of the data [11].
The recent development of energy-efficient hardware with AI acceleration enters the fog class of the devices, putting Fog Computing in the middle of the interest of IoT application development and extending new horizons to them. Fog Computing is more energy efficient than raw data transfer to the cloud and back, and in the current scale of the IoT devices, the application is meant for the future of the planet Earth. Fog Computing usually also brings a positive impact on IoT security, i.e. sending to the cloud preprocessed and depersonalised data and providing distributed computing capabilities that are more attack resistant.

Edge Computing

Recent development in hardware, power efficiency and a better understanding of the IoT data nature, including such aspects as, i.e. privacy and security, led to solutions where data is being processed and preprocessed right to their source in the Edge class devices. Edge data processing on end-node IoT devices is crucial in systems where privacy is essential and sensitive data is not to be sent over the network (i.e. biometric data in a raw form). Moreover, distributed data processing can be considered more energy efficient in some scenarios where, i.e. extensive, power-consuming processing can be performed during green energy availability.

Cognitive IoT Systems

According to [12], Cognitive IoT, besides a proper combination of hardware, sensors and data transport, comprises cognitive computing, which consists of the following main components:

  • understanding – in the case of IoT, it means systems' capability to process a significant amount of structured and unstructured data, extract the meaning of the data – produce a model that binds data to reality;
  • reasoning – involves decision-making according to the understood model and acquired data;
  • learning – creating new knowledge from the existing, sensed data and elaborated models.

Usually, cognitive IoT systems or C-IoT are expected to add more resilience to the solution. Resilience is a complex term and is differently explained under different contexts; however, there are standard features for all resilient systems. As a part of their resilience, C-IoT should be capable of self-failure detection and self-healing that minimises or gradually degrades the system's overall performance. In this respect, the non-resilient system fails or degrades in a step-wise manner. In case of security issues, that system should be able to change its security keys, encryption algorithms and take other measures to cope with the detected threats. Self-optimisation abilities are often considered part of the C-IoT feature list to provide more robust solutions. Recent development in the Fog and Edge class devices and the efficient software leverage cognitive IoT Systems to a new level.

All three approaches, from cloud to cognitive systems, focus on adding value to IoT devices, system users and related systems on-demand. Since market and technology acceptance of mobile devices is still growing, and the amount of produced data from those devices is growing exponentially, mobility as a phenomenon is one of the main driving forces of the technological advancements of the near future.

Data Management Aspects in IoT

Data management is a critical task in IoT. Due to the high number of devices (things) already available, that is tens of billions, and considering the data traffic generated by each of them through, i.e. sensor networks, infotainment (soft news) or surveillance systems, mobile social network clients, and so on, we are now even beyond the ZettaByte (ZB 2^70, 10^21 bytes) era. This opened up several new challenges in (IoT) data management, giving rise to data sciences and big data technologies. Such challenges have not to be considered as main issues to solve but also as significant opportunities fuelling the digital economy with new directions such as Cloudonomics [13] and IoTonomics, where data can be considered as a utility, a commodity to manage, curate, store, and trade appropriately. Therefore, properly managing data in IoT contexts is not only critical but also of strategic importance for business players as well as for users, evolving into prosumers (producers-consumers).

From a technological perspective, the main aspects of dealing with IoT data management are:

  • data source: data generation and production is a relevant part of IoT, involving sensors probing the physical system. In a cyber-physical-social system view, such sensors could be virtual (e.g. software) or even human (e.g. citizens, crowdsensing). The main issues in data production are related to the type and format of data, heterogeneity in measurements and similar issues. Semantics is the key to solving these issues, also through specific standards such as Sensor Web Enablement and Semantic Sensor Network;
  • data collection/gathering: once data are generated, these should be gathered and made available for processing. The collection process needs to ensure that the data gathered are defined and accurate so that subsequent decisions based on the findings are valid. Some types of data collection include census (data collection about everything in a group or statistical population), sample survey (collection method that includes only part of the total population), and administrative by-product (data collection is a byproduct of an organisation’s day-to-day operations). Usually, wireless communication technologies such as Zigbee, BlueTooth, LoRa, Wi-Fi and 3G/4G networks are used by IoT smart objects and things to deliver data to collection points;
  • filtering: is a specific preprocessing activity, usually performed at data source or data collector (IoT) nodes (e.g. motes, base stations, hotspots, gateways), aiming at cleaning noisy data, filtering noise and not helpful information;
  • aggregation/fusion: to reduce bandwidth before sending data to processing nodes, these are further elaborated, compressed, aggregated and fused (sensor/data fusion) to reduce the overall volume of raw data to be transmitted and stored;
  • processing: once data are adequately collected, filtered, aggregated, and fused, they can be processed. Processing can be both local and remote and usually includes preprocessing activities to prepare data for real processing. Local processing, when possible, is mainly tasked with a fast, lightweight computation on edges (Edge computing) and in the Fog layer, wherever possible, quickly providing results and local analytics. More complex computations are usually demanded to remote (physical or virtual) servers provided by local nodes (e.g. communication servers, cloudlets) in a Fog computing fashion or by Cloud providers as virtual machines hosted in data centres. This kind of computation can also involve historical data, providing global analytics, but hardly meets time-constrained applications and real-time requirements;
  • storage/archive: remote servers are also used for permanently storing and archiving data, making these available for further processing, even to third parties. The database is often used for that, mainly based on distributed, NoSQL key-store technologies to improve reliability and performance;
  • delivering/presentation/visualization: processing activity results must then be delivered to requestors and users. These have to be, therefore, adequately organised and formatted, ready for end-users. IoT data visualisation is becoming an integral part of the IoT. Data visualisation provides a way to display this avalanche of collected data in meaningful ways that clearly present insights hidden within this mass amount of information;
  • security and privacy: data privacy and security are among the most critical issues in IoT data management. Good results and reliable techniques for secure data transmission, such as TLS and similar, are available. This way, IoT data security issues mainly concern [14] securing IoT devices, since they are usually resource-constrained and therefore do not allow to adopt traditional cryptography scheme to data encryption/decryption. Data privacy and integrity should also be enforced in remote storage servers, anonymising data and allowing owners to properly manage (monitoring, removing) them while ensuring availability. Indeed, security and privacy issues vertically span the whole IoT stack. A promising technique to address IoT security issues, attracting growing interest from both academic and business communities, is blockchain [15].

IoT Application Domains

Application domains of the Internet of Things solutions are vast. Most prominent applications include (among others) [16]:

  • building and home automation,
  • smart water,
  • internet of food,
  • smart metering,
  • smart city (including logistics, retail, transportation),
  • industrial IoT,
  • precision agriculture and smart farming,
  • security and emergencies,
  • healthcare and wellness (including wearables),
  • smart environment,
  • energy management,
  • robotics,
  • smart grids.

Smart Homes are one of the first examples that come to mind when discussing Internet of Things domain applications. Smart home benefits include reduced energy wastage, the quality and reliability of devices, system security, reduced cost of basic needs, etc. Some home automation examples are environmental control systems that monitor and control heating, ventilation, air conditioning and sunscreens; electrical charging of vehicles; solar panels for electrical power and hot water; ambient lighting control, smart lighting for aquaria; home cooking and food ordering; access control (doors, garage, gate); smart plant irrigation systems (both indoors and outdoors); baby monitoring; timed pet food dispensers; monitoring perishable goods (for example, in the refrigerator); household items remote monitoring (for instance, of washer cycle status); tracking and proactive maintenance scheduling (such as, i.e. electric car charging); event-triggered task execution. Home security also plays a significant role in smart homes. Examples of applications are automatic door locks, sensors for opening doors and windows, pressure, motion and infrared sensors, security cameras, notifications about security (to the owner or the police) and fitness-related applications.

In Smart City, multiple IoT-based services are applied to different areas of urban settings. The aim of the smart city is the best use of public resources, improvement of the quality of resources provided to people and reduction of operating costs of public administration [17]. A smart city can include many solutions like smart buildings, smart grids for improving energy management, smart tourism, monitoring of the state of the roads and occupation of parking lots, public transportation optimisation, public safety, environment monitoring, automatic street lighting, signalling with smart power devices, control of water levels for hydropower or flood warnings, electricity-generating devices like solar panels and wind turbines, weather monitoring stations. Transportation in smart cities may include aviation, monitoring and forecasting of traffic slowdowns, timetables and current status, navigation and route planning, as well as vehicle diagnostics and maintenance reports, remote maintenance services, traffic accident information collection, fleet management using digital tachographs, smart parking, car/bicycle sharing services [18]. IoT in transportation makes cars interconnected, particularly in the approaching autonomous vehicles era.

Smart Grid is a digital power distribution system. This system gathers information using smart meters, sensors and other devices. After these data are processed, power distribution can be adapted accordingly. Smart grids deliver sustainable, economical and secure electricity supplies efficiently.

In Precision Agriculture and Smart Farming IoT solutions can be used to monitor the moisture of the soil and conditions of the plants, control microclimate conditions and monitor the weather conditions to improve farming [19]. The goal of using IoT in agriculture is maximising the harvest, reducing operational costs, being more efficient, and reducing environmental pollution using low-cost automated solutions. An interaction between the farmer and the systems can be done using a human-machine interface. In the future smart precision farming can be a solution for such challenges as increasing worldwide demand for food, a changing climate, and a limited supply of water and fossil fuels [20].

Internet of Food integrates many of the aforementioned techniques and encompasses different stages of the food delivery chain, including smart farming, food processing, transportation, storage, retail, and consumption. It provides more safety and improved efficiency at each food production and consumption stage, including reduced waste and increased transparency.

Similar to precision agriculture, which is part of IoT in industry, Smart Factories also tend to improve manufacturing by monitoring pollutant gas emissions, locating employees and with many other solutions.

Industrial IoT and smart factories are part of the Industry 4.0 revolution. In this model, modern factories can automate complex manufacturing tasks, thanks to the Machine-To-Machine communication model, which provides more flexibility in the manufacturing process to enable personalised, short-volume product manufacturing easily.

In the healthcare and wellness, IoT applications can monitor and diagnose patients and manage people and medical resources. It allows remote and continuous monitor the vital signs of patients to improve medical care and wellness of patients [21]. An essential part of smart welfare is wearables, including wristbands and smartwatches that monitor the activity level, heart rate and other parameters. Smart healthcare includes remote monitoring, care of patients, self-monitoring, smart pills, smart home care, Real-Time Health Systems (RTHS) and many more. Medical robotics can also be part of the healthcare IoT system that includes medical robots in precision surgery or distance surgery; some robots are used in rehabilitation and hospitals (for example, Panasonic HOSPI [22]) for delivering medication, drinks, etc. to patients.

Wearables used in IoT applications should be highly energy efficient, ultra-low power and small-sized. Wearables are installed with sensors and software for data and information collected about the user. Devices used in daily life like Fitbit [23] are used to track people's health and exercise progress in previously impossible ways, and smartwatches allow to access smartphones using this device on the wrist. But wearables are not limited only to wearing them on the wrist. They can also be glasses equipped with a camera, a sports bundle attached to the shoes or a camera attached to the helmet or as a necklace [24].

Introduction to the IoT Microcontrollers

[ktokarz][✓ ktokarz, 2023-08-23]Describe in general IoT MCUs: CPU, FPU, RAM, Storage (flash), GPIO, Interrupts system, DMA, built-in radio interfaces (if any).

IoT device is, in almost all cases, based on microcontroller. A microcontroller, often called a single-chip computer, is an integrated circuit that incorporates all units required to function as the computer. It includes a central processing unit (CPU), memory for programs, memory for data, inputs, outputs, timers, serial communication ports and other peripherals. Complex microcontrollers, called embedded processors, can include more processor cores, display controllers, advanced internal data transfer mechanisms (like DMA), programmable connections between modules, specialized coprocessors for ciphering and deciphering, compression and decompression, video and audio coding and decoding, and other modules. Microcontrollers are even more complex in the IoT world due to wireless networking capability. A complex microcontroller equipped with an internal radio communication module is also known as a System on Chip (SoC).

Although many microcontrollers or SoCs are called processors, historically, the processor is the name of the element of the CPU functionality only. It must be connected to memory and peripherals to form a fully functional computer. On the other hand, a microcontroller, embedded processor or System on Chip can work without any external elements; it just requires the power supply to operate.

The typical microcontroller includes general-purpose units like:

  • CPU core,
  • Program memory,
  • Data memory,
  • Timers, Counters,
  • Interrupt controller,
  • I/O ports,
  • Serial synchronous and asynchronous communication ports,
  • Analog to Digital converter,
  • PWM (Pulse width Modulation unit for Digital to Analog conversion),
  • DMA controller,
  • Supervisory units (Watchdog, Reset, Brownout).

Embedded Processor or System on Chip can contain also:

  • Network interface,
  • USB controller,
  • Memory interface module,
  • Floating point unit (FPU),
  • Cryptographic module,
  • Other application-specific extensions.

The CPU core is the unit that executes the main program. It controls program flow, executes general-purpose instructions, calculates addresses, and processes integer values. For fast floating point calculations, an FPU coprocessor is built-in. It executes instructions that perform calculations on real numbers and advanced mathematical functions. The program instructions are fetched from program memory, usually implemented as internal or external flash memory. Data is stored in internal data memory implemented as static RAM. If more memory is needed, some microcontrollers have a memory management unit that allows them to connect external DRAM memory. Flash memory is often used as a place for file storage. Timers and counters are units that help to generate pulses of specified length and square signals of specified frequency. They can also measure delays and synchronise the work of other modules like serial ports, converters, and displays. Timers can generate pulse width modulated signals to control the speed of motors and light brightness. Microcontrollers have digital input and output ports to connect other elements of the systems. Connecting external sensors to collect information from the surroundings and output devices to manipulate environmental parameters is possible. Analogue inputs can read the voltage value generated by simple sensors. Serial communication ports are used to connect more complex sensors and displays to communicate with the user or another computer system. An interrupt controller is a unit that automatically executes subroutines responsible for handling tasks specific to the hardware that signalled the situation that needs the processor's attention. The processor doesn’t have to waste execution time by periodically checking if there is a need to take care of the device. It helps to make the code more efficient and reliable. Supervisory units help to recover from some abnormal situations. Watchdog resets the processor in case the software hangs up. Brownout detector constantly monitors the power supply voltage. It stops the processor if the voltage is too low for proper operation to avoid execution errors, flash write errors, and other malfunctions. Supervisory interfaces like JTAG allow writing the programs into flash memory and debugging the code. Direct Memory Access (DMA) module performs memory operations without processor intervention. It is usually used for copying data blocks between memory and other peripheral units. For example, data from the network unit is stored automatically in the buffer, and the CPU is informed while the data transfer is complete.

Details of the internal construction and operation of many internal modules of popular microcontrollers are described in further chapters of this book.

Introduction to Embedded Programming

IoT systems share programming paradigms with embedded systems. Each microcontroller manufacturer has its own set of tools (usually called SDK or Development Framework) that frequently contain an IDE dedicated to the platform. There are some cross-platform solutions and frameworks, however.
Programming languages include:

  • C/C++ - undoubtedly the most popular, versatile, yet demanding programming language. With modern supporting tools such as syntax highlights, code samples, code generators (AI-based) and instant syntax checking, C/C++ programming became relatively easy but still requires solid software development foundations. On the other hand, it is probably the only programming language that is natively supported with hardware debugging features. C/C++ bare metal programming allows the developer to control all MCU features on the lowest level and implement energy-efficient, fast and compact solutions.
  • Java and Javascript - with low entry-level for developers, usually represented by the variation of NodeJS, limited and applicable to beginners. Within the constraints of the interpreter, it provides rapid prototyping and the fastest market delivery but the lowest flexibility and extensibility beyond what the manufacturer plans. Also, the Java development framework implemented in the microcontroller is compact because of the constrained resources. Usually, it does not keep standards, so the feature of the portability of the code is somewhat limited.
  • Python (Micropython) - similarly to Java, offers an easy start but low flexibility and control over the hardware. Acceptable for prototyping.
  • Other.

IoT and Embedded Systems Programming Models

IoT device programming can be done on a variety of levels. Below we present the most popular models and briefly discuss their pros and cons.

Bare Metal Programming

The bare metal programming model is where the software developer builds firmware (usually from scratch or based on a stub generated by the SDK) and flashes it to the MCU. The MCU usually does not contain software other than technical ones necessary for starting and updating the device, i.e. a bootloader. The developer must implement all algorithms, communication, interfacing, storage, etc., on a low level. They may use 3rd party libraries to implement it, which speeds up development significantly. There is no operating system running in the background. Eventually, it comes with the firmware as part of it, as included by the developer, i.e. FreeRTOS [25].

Bare metal programming applies first to the Edge class devices, rarely to the Fog class.

Bare metal programming requires a good understanding of the hardware configuration of the IoT device as well as the configuration of the software development toolchain. The MCU manufacturer usually provides SDK and related tools, but there do exist middleware solutions (such as PlatformIO [26]) that significantly simplify installation.

In most cases, source code is written in C or C++ language or their combination (e.g. in the case of the STM). The development process for bare metal programming is present in the following figure 2 and its features are discussed in table 2. In short, it requires developing, compiling and uploading the firmware to the device's flash memory. Programming uses a programmer (physical or Over The Air - OTA, virtual interface). The bare metal model usually provides the capability of hardware development.

 Bare metal IoT firmware development process
Figure 2: Bare metal IoT firmware development process

The bare metal programming model is considered the only one to enable developers to have absolute control over the hardware on a very low level. On the one hand, it brings opportunities to implement non-standard solutions and optimal code in terms of compactness and efficiency; on the other, it increases time-to-market delivery. Recent advances in development supporting tools (i.e. AI-based code generation), wide availability of the libraries, standardisation of their presence and automated management, such as, e.g. in PlatformIO Library Management [27] significantly lower this time.

Table 2: Bare metal programming pros and cons
Pros Cons
Absolute control over hardware Need to implement all from scratch
Secure, low vulnerability Requires good hardware understanding
No bottlenecks Requires advanced programming skills
Efficient and compact code Requires complex development environment
Fastest, no overhead of the middleware Possibility to brick the device during flashing
Good community support Time consuming implementation
Highly flexible, enables the developer to prepare non-standard solutions
Provides hardware debugging capabilities
Energy efficient, it gives control over low-level, energy-saving mechanisms (waitstates, sleep modes, radio power, etc.)
In the IoT world, it is common to distribute firmware remotely (OTA - Over The Air). For this reason, it is pretty frequent that the flash memory of the IoT device is split in half into two partitions, each containing a different version of the firmware (old and new). OTA mechanism flashes an inactive partition, and then the bootloader swaps them during the reboot of the device once flashing is done. If new firmware fails to boot, the bootloader swaps the partition back to run the old version, reboots the device, and notifies about the update error.

Script Programming with Middleware

Opposite to bare metal programming, script programming does not involve compilation or firmware burning into the flash memory. This programming model uses interpreted languages such as Python (actually Micropython: an edition of Python for microcontrollers dedicated to constrained devices), NodeJS, Javascript, Java, C#, etc. A virtual machine middleware (programming language interpreter) running bare metal (installed as firmware) or as a part of the operating system (if any), and the developer prepares an algorithm as a script, usually in a textual form, later uploaded and executed on the device. The middleware brings an overhead on execution; thus, this solution is intended for not so constrained IoT devices, still acceptable for Edge and quite common for Fog class. It requires much more CPU, RAM and storage than bare metal programming, has limitations from the interpreter implementation and only indirectly accesses hardware. It is not suitable for real-time solutions.

Scripting programming is common for more powerful Edge devices and almost the first choice for Fog class devices.

The development process for scripting programming is present in the following figure 3 and its features are discussed in table 3. In short, it requires limited SDK (or none), but debugging is complex, if possible.

 IoT programming process
Figure 3: Scripting IoT programming process

This programming model is suitable wherever standard solutions are implemented and where code execution efficiency is not critical, and there is no demand for real-time operations; eventually, the IoT device is unconstrained, providing developers with decent CPU (e.g. modern ARM), RAM and storage. Note those solutions are usually less energy efficient than bare metal programming; still, they offer great flexibility in algorithm implementation, far beyond a predefined list of choices or limited configuration as presented in the following section. On the other hand, it speeds up delivery time to the market because of the ease of implementation, the lack of need to install the complex software development environment and the high level of abstraction.

Table 3: Scripting programming pros and cons
Pros Cons
Better suitable for beginners Not optimal because of the middleware overhead on execution
Uses higher abstraction level Not suitable for real-time operations
Uses high-level programming languages Limited hardware interfacing and features to those exposed by the middleware
Usually does not involve complex SDK/development environment Limited and non-optimal energy efficiency management
Flexible enough to implement complex algorithms Low extendibility
Rapid development Middleware updates used to cause the need to adapt script with algorithm
Hardware debugging is tricky or not possible at all

Configuring Firmware

Several middleware (IoT frameworks) are available for various IoT devices. This development model focuses on reconfiguring the sealed firmware delivered “as is” using some configuration interface or script (or both). Eventually, modifying and recompiling it yourself is possible if it is open source. Still, the process is usually very complex, and understanding all relations and development toolchain sometimes is more complicated than developing a solution from scratch as a bare metal. Proprietary frameworks do not bring this opportunity at all and are delivered “as is” with a predefined set of features. The development is limited to reconfiguring the elements from simply switching them on and off through setting up access and credentials, even up to the GPIO assignment. This usually does not bring capabilities to modify the algorithm, eventually to choose a behaviour from the predefined list proposed by the middleware author. Such a model does not bring debugging capabilities; finally, simple tracking with error codes and log files (if at all). Moreover, in many scenarios, middleware is dependent on some external resources (i.e. authorisation via a cloud or firmware updates delivered with this channel).

Middleware configuration model is applied to both Edge and Fog class devices, exposed via IoT frameworks. Sometimes it also involves the cloud part of the solution.

The development process for the middleware configuration model is present in the following figure 4 and its features are discussed in table 4.

 Middleware configuration process
Figure 4: Middleware configuration process

Configuration range varies among IoT frameworks but commonly requires compatible hardware. Proprietary firmware provides sealed configuration software and encryption; thus, it virtually excludes any non-standard modifications or makes them very complex. IoT hardware used to be compatible with more than one firmware, and proprietary ones can be replaced with another open source, i.e. Tasmota [28]. Configuration in proprietary middleware scenarios can be provided indirectly via a cloud solution that raises serious questions about privacy (i.e. configuring your private WiFi router credentials via a public or 3rd party cloud, not directly to the device).

Table 4: Middleware configuration model pros and cons
Pros Cons
Easy to use even for beginners Limited number of use scenarios
Consistent environment (configuration, use) common look and feel Problems appearing hard to solve in case of failure
No need for SDK, configuration tools use plain text, browsers or apps Low flexibility - limited support for hardware (only proprietary or limited compatibility in the case of the open source)
Manufacturer's support (for proprietary) but usually for a limited time and shorter compared to open source solutions Doubtful privacy, in particular when a public cloud is in use
Usually reliable Lack of help once Manufacturer's maintenance period is finished
During the maintenance period, updates are given by the vendor
In this book, we focus on the bare metal programming model using the C/C++ model, but we also present some aspects of scripting programming and review some IoT frameworks that are exposed with the middleware configuration model.

Introduction to the Programming Frameworks

In the beginning, it is essential to distinguish an IoT Framework that is a set of tools, firmware for a variety of devices, sometimes also hardware, delivered as is and providing developers with configuration capabilities on the high abstraction level from the Programming Framework that is related to the low-level programming, here in C/C++, referred to as an SDK. SDK tends to be a narrower definition than a programming framework as the former one contains not only SDK but also tools, development toolchain and code organisation rules.
This chapter presents and discusses programming frameworks (SDKs and source code organisation) that define how the IoT code is organised on the low level in the Bare Metal programming model for Edge class devices.

Almost every MCU (microchip/microcontroller) vendor develops its own SDK, providing programmers with a specific programming framework. It is worth nothing to mention that, in many cases, it follows the general programming construction of the source code for C or C++, such as below:

int main() {
    std::cout << "Hello IoT!";
    return 0;
}

A common approach is to use a GUI to automate the generation of the source code stub that contains the hardware-specific configuration, i.e. timers, GPIOs, and interrupts, to avoid monotonous and complex tasks and speed up time to market.
Still, as hardware differs, it is particular for each platform, and usually, software development requires a rigorous approach to inject user-specific code only in predefined locations. Otherwise, it may break source code or even delete it when re-generating configuration using SDK tools and automation. Sample main() function for the STM32 MCU is presented below. Developers are intended to fill their code only in predefined areas, such as starting from USER CODE BEGIN Init and finishing before USER CODE END Init; otherwise, the source code will be gone when updating the configuration:

int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* USER CODE END 1 */
  HAL_Init();
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
  SystemClock_Config();
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
  MX_GPIO_Init();
  MX_LPUART1_UART_Init();
  MX_NVIC_Init();
  /* USER CODE BEGIN 2 */
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
      nUARTBufferLen = sprintf((char*)tUARTBuffer, "Hello World!\n\r");
      HAL_UART_Transmit_IT(&hlpuart1, tUARTBuffer, nUARTBufferLen);
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Studying specific platforms is time-consuming, and as each vendor has its own approach, knowledge and source codes are usually not portable between microcontrollers.
Specific frameworks for hardware vendors are (among others):

  • Espressif ESP8266:
    • ESP8266 RTOS SDK,
    • ESP8266 Non-OS SDK,
    • Arduino.
  • AVR/Atmel:
    • AVR Studio (Atmel Studio),
    • Arduino.
  • Espressif ESP32:
    • ESP-IDF,
    • Arduino.
  • Nordic Semiconductors nRF52:
    • Mbed,
    • Zephyr RTOS,
    • nRF5 SDK,
    • Arduino.
  • ST Microelectronics STM32 series:
    • Mbed,
    • CMSIS,
    • Zephyr RTOS,
    • Registers programming model (RAW),
    • STM32Cube (HAL),
    • Arduino.

Typical C++ code, as presented above, is a single-pass execution. On the other hand, IoT devices used to work infinitely, handling their duties such as reading sensors, communicating over the network, sending and receiving data, routing messages and so on, thus, requiring setting up an infinite while (1) loop for processing. Many tasks need to be done in parallel, so it is common to include a task scheduling mechanism to run multiple tasks asynchronously. A common is to use the FreeRTOS [29] or its modified versions for the specific hardware platform provided by the hardware vendor, i.e. as in the case of the ESP32 [30] to provide support for multicore MCUs.

Name FreeRTOS may be misleading because it can be understood as a general purpose operating system (GPOS) suggesting it runs in the background before your application starts as Windows or Linux does. FreeRTOS as Embedded Operating System (OS for embedded systems and microcontrollers) is included as a C/C++ library in the source code and built into the firmware and algorithms. It provides similar functionalities as the GPOS kernel with task handling, memory management, file system, etc.

Arduino Framework

Observing the list of software frameworks above, one can easily find that many platforms have common frameworks, but the Arduino framework is present for all of them. Arduino framework is a cross-platform approach providing a slightly higher level of abstraction over dedicated software frameworks, and it is the most popular among hobbyists, students, professionals and even researchers at the moment. Arduino Framework is a reasonable balance between uniform code organisation and elements of cross-hardware HAL, still bringing opportunities to access hardware on a low level and get the advantage of the advanced features of modern IoT microcontrollers such as, e.g. power management. Most hardware vendors support this framework natively, and it has become almost an industry standard. Some advanced hardware control may require integration or other native frameworks, anyway. Still, the Arduino framework has real-time capacity. It is powerful and flexible enough to handle most IoT-related tasks, and most of all, it has excellent community support with dozens of software libraries, examples and applications worldwide.

A dummy C/C++ code for the Arduino framework looks as follows:

void setup()
{
 
}
 
void loop()
{
 
}

The void setup() function is executed only once after the microcontroller reboots. Its purpose is to initialise, instantiate objects, read configuration, check working conditions, and so on: generally, all tasks that are to be executed only once in a work cycle of the IoT device.
The void loop() function is executed in a loop automatically and infinitely once a single pass is finished. Its purpose is to implement repeating tasks such as periodical reading of a sensor and sending the data to the cloud. There is no need to implement a dummy while(1) inside the loop(); moreover, it is usually not advised or even forbidden. It is because, for every execution of the loop() statement, many other tasks, such as handling communication, may be executed once. Making a single pass of the loop() function infinite (i.e. with implementing an infinite while(1) loop, could cause starvation of the other underlying processes the framework handles, such as network communication, embedded protocols handling, etc.).

The book presents code and examples in the Arduino framework context for Edge class devices but also for Fog class devices (scripting). Wherever other framework is used, it will be clearly stated. Note, following introduction to the C and C++ programming and task handling contents, are universal and can be applied to the other frameworks, whether directly or indirectly, with some adaptation on the code level.

Software Development Tools and Platforms

Software development in the bare metal model requires a development toolchain installed on the developer's computer. The vendor of the MCU usually provides a set of tools. This set frequently includes a dedicated compiler, linker, library management tools, configuration tools, debugger software, etc. These tools are command lines in most cases. Using a GCC [31] C/C++ compiler is also quite common. On top of it, a GUI with a rich UI interface is built to simplify software development. Some vendors provide their own GUIs, such as, e.g. STMicroelectronics' STM32CubeIDE (image 5), while others use already available universal code editing solutions and integrate with them, i.e. in the form of plugins or extensions.

Vendors barely develop their own GUI solutions from scratch; instead, they adapt existing open-source ones, i.e. STM32CubeIDE is built on top of the Eclipse IDE [32].
 Eclipse for STM32 developers
Figure 5: STM32CubeIDE: Eclipse for STM32 developers

Because documentation for the command line tools composing SDK is usually available, there are also universal solutions that enable developers to use a single GUI environment for various tasks and microcontrollers, switching among them quickly, such as Visual Studio Code (figure 6). Each platform requires its dedicated toolchain, anyway, and integration with universal code editors such as the aforementioned VS Code may be tricky. Luckily, there are tools to help with the automated installation of all required components, such as PlatformIO [33], that we describe below.

 Universal development environment
Figure 6: VS Code: a universal development environment

As the Arduino programming framework became a cross-platform standard, vendors provided low-level libraries implementing standard functionalities such as embedded communication protocols (Serial, SPI, I2C, 1Wire) and networking communication. Arduino, a manufacturer of popular development boards, provides an IDE (figure 7: Arduino IDE [34]) that is intended to be an entry-level development environment. It can be extended beyond genuine Arduino boards, i.e. with the Espressif toolchain for ESP8266 and ESP32. This software, however, is very limited in features and is suitable only for simple projects.

 Arduino IDE: an entry-level IDE for beginners
Figure 7: Arduino IDE: an entry-level IDE for beginners
We suggest starting with VS Code and PlatformIO over Arduino IDE, even if you're a beginner.

Developers Middleware and Support Tools

A number of additional tools usually come with the development toolchain provided by the hardware vendors. They include programmers (flashers, injecting firmware into the IoT device), configuration tools, power consumption calculators, etc. Installation is not always straightforward, and updating is tricky. Developers who use a variety of platforms (MCUs) struggle with instant updates, browsing the web for tools and sources. Moreover, handling libraries they use for development is time-consuming and involves instant monitoring of changes, manual copy-paste operations on files, etc. And it has to be done for every project individually.

PlatformIO

The solution is a developer's middleware that integrates with selected IDE and helps to install, configure and maintain toolchains for hardware, software development libraries, and also contains a set of additional tools (i.e. serial port monitor, JTAG debugger, code repository integration, collaboration tools, remote development, etc.). As mentioned above, one example of a handy middleware for IoT and embedded development is PlatformIO. It is a command-line toolset that provides a whole ecosystem for virtually any hardware platform; it still uses the vendor's proprietary toolchains. It perfectly integrates with Visual Studio Code (among others) via VS Code's extension (plugin) systems. VS Code works also as a GUI for PlatformIO. In the following figures, we present its look and UI when integrated with Visual Studio Code (figures 8, 9 and 10).

 VSCode with PlatformIO: starting page
Figure 8: VSCode with PlatformIO: starting page
 VSCode with PlatformIO: library management
Figure 9: VSCode with PlatformIO: library management
 VSCode with PlatformIO: toolchain management
Figure 10: VSCode with PlatformIO: toolchain management

A PlatformIO-enabled IoT project is a set of files with a platformio.ini file in the root folder and main.cpp in the ./src/ subfolder (as, i.e. in the figure 8, project folder tree is to the left)). The platformio.ini file describes all technical parts of the project: the hardware platform, the method of uploading the firmware (usually via a serial port), software libraries that are included in the code and should be automatically pulled from the libraries repository during compilation and many other options [35]. Sample platformio.ini file is presented in the code below:

[env:d1_mini]
platform = espressif8266
board = d1_mini
framework = arduino
upload_port = /dev/ttyUSB0
upload_speed = 9600
monitor_port = /dev/ttyUSB0
lib_deps = 
	arduino-libraries/LiquidCrystal@^1.0.7
	adafruit/Adafruit Unified Sensor@^1.1.7
	adafruit/DHT sensor library@^1.4.4

This code configures the ESP8266 (Espressif) hardware project, with specific developer board D1 Mini and programming done in the “Arduino” framework development model.
Communication with the IoT device is via serial port (here /dev/ttyUSB0 for Linux or, i.e. COM3 for Windows) and uses the same port for monitoring (serial port monitor for tracing messages from the MCU and code).
It uses three libraries registered in the library registry for PlatformIO: LiquidCrystal, Adafruit Unified Sensor and DHT sensor library, with explicit versions. PlatformIO's Library Manager automatically checks for updates and proposes to update libraries to the latest available if a version is not explicitly stated. The Library Registry in the PlatformIO is a repository of the Gitlab project, available online [36]. Libraries are currently held per project instead of shared between projects.
At the start of the PlatformIO GUI and then periodically, it checks for PlatformIO updates and development toolchain updates, proposing to update them when a new version is available.

C/C++ Language Embedded Programming Fundamentals

The following sub-chapters cover programming fundamentals in C/C++, which comply with most C/C++ notations. Those who feel comfortable in programming will find these chapters somewhat introductory, while for those having no or little experience, it is highly recommended to cover this introduction. This chapter and its sub-chapters target the basics and general syntax of C/C++ programming for different platforms, including Arduino, Espressif, Nordic, STM32, and partially for Raspberry Pi devices; however, in any case, the programming environment configuration is different for every platform. The Arduino programming framework is common for many MCU manufacturers of the IoT Edge class devices in bare metal programming mode, even if bringing some overhead and does not let the developer push the devices to their limits. Thus we refer mostly to this one. To enjoy full power, efficiency and control of the specific device, one needs to use a dedicated SDK and Framework, but for teaching purposes and many even professional applications, Arduino Framework is suitable and a good balance between the cost of the development and the result.

This manual refers to the particular versions of the software available at the moment of writing this book. Accessing specific features may change over time along with the evolution of the platform. Please refer to the attached documentation (if any) and browse Internet resources to find the latest guidance on configuring specific development platforms when in doubt.

Data Types and Variables

Almost every computer program manipulates the data. Data representation in the program is variable. In C/C++, the variable needs to be defined before using it, giving it some name and assigning chosen type, dependent on the kind of data. Here we show some common data types and how to use variables.

Data Types

Data type specifies how it is encoded and represented in the computer memory. For example, integer numbers are binary-encoded, the texts are represented as a series of ASCII-encoded characters, and real numbers have a particular encoding scheme that consists of two binary numbers - mantissa and exponent. Other data types are tables that consist of elements of the same type or structures with elements of different types. There are plenty of different data types, some of them are predefined, but user-own types can be defined based on existing ones. Creating a variable requires specifying its type, which determines its place in the memory of the microcontroller and also the way how it can be used. Further will be viewed the most used ones together with examples of defining variables.

  • byte – a numeric type of 8 bits that stores numbers from 0 to 255.
byte exampleVariable;

The example above defines a variable, reserves its memory, and assigns the memory address to the variable name. It is also possible to give the variable the initial value as below:

byte exampleVariable = 123;
  • int – the integer number. Its size depends on the microcontroller class. In the case of AVR (Arduino), it consists of 16 bits that can contain values from –32 767 to 32 768. In ARM-based microcontrollers (like STM32), its size is 32 bits.
int exampleVariable = 12300;
  • float – a data type for real numbers that uses 32 bits and stores numbers approximately from –3.4 × 10^38 to 3.4 × 10^38.
float exampleVariable = 12300.546;
  • array – a set of data of the same type that can be accessed using a serial number or index. The index of the first element is always 0. The values of an array can be initialized at the definition of it or set during the execution of the program. In the following example, the array of four elements with the name “first array” and data type int has been created. The value of the array with an index of 0 will be 12, and the value with an index of 3 will be 15.
int firstArray[] = {12,-3,8,15};

Square brackets of the array can be used to access some value in the array by index. In the following example, the element with index 1 (that is –3) is assigned to the secondVariable variable.

int secondVariable = firstArray[1];

An array can be quickly processed in the loop. The following example shows how to calculate the sum of all elements from the previously defined array (for statement will be explained in detail in the following chapters).

//The loop that repeats 4 times
int sum = 0;
for(int i = 0; i < 4; i = i + 1){         
     sum = sum + firstArray[i]; 
}

The loop in the example starts with index 0 (i = 0) and increases it by 1 while smaller than 4 (not including). That means the index value will be 3 in the last cycle because when the i equals 4, the inequality i < 4 is not true, and the loop stops working.

  • bool – the variables of this data type can take values TRUE or FALSE. Arduino environment allows the following values to these variables: TRUE, FALSE, HIGH (logical 1 (+5 V)) and LOW (logical 0 (0 V)).

Data Type Conversion

Data type conversion can be done using multiple techniques – casting or data type conversion using specific functions.

  • Casting – cast operator translates one data type into another type straight forward. The desired variable type should be written in the brackets before the variable data type, which needs to be changed. In the following example, where the variable type is changed from float to int, the value is not rounded but truncated. Casting can be done to any variable type.
int i;
float f=4.7;
 
i = (int) f; //Now it is 4
  • Convertingbyte(), char(), int(), long(), word(), float() functions are used to convert any type of variable to the specified data type.
int i = int(123.45); //The result will be 123
  • Converting String to float – function toFLoat() converts String type of variable to the float. The following example shows the use of this function. If the value cannot be converted because the String doesn't start with a digit, the returned value will be 0.
String string = "123fkm";
float f = string.toFLoat(); //The result will be 123.00
  • Converting String to Int – function toInt() converts String type of variable to the Int. In the following example, the use of this function is shown.
String string = "123fkm";
int i = string.toInt(); //The result will be 123

Defining New Types

Typedef Specifier

A typedef specifier can give another name for existing types or declares a new one. Renaming types is possible, but software development frameworks used have a number of aliases already. It is helpful, however, when combined with enumerations, classes and structures to give them reasonable names and re-use them later in the code to improve their readability. We present more details on structures in the chapter Structures and Classes, but here is an example presenting a reasonable use of the typedef specifier.

typedef struct {int x; int y;} tWaypoint; //Declare complex type named waypoint
...
//Declare a variable of the type of tWaypoint
tWaypoint wp1;
Enum Declaration

Enumerations are helpful to give meaning to the integer values and present some logic in a code instead of putting numbers into it. It can be, i.e., the device's state, error code, etc. In the case a new enumeration is needed, it is possible to declare one using the enum keyword and specifying a list:

enum errorcodes {ER_OK, ER_DOWNLOAD, ER_UPLOAD, ER_NOWIFI};  //define enumeration
...
errorcodes Errorcode;                                        //declare a variable
...
Errorcode = ER_DOWNLOAD;                                     //assign a value

The default numbering starts with 0 (ER_OK=0) and increases by 1 with every next item on the enumeration list. However, explicitly defining values represented by the item labels is possible.

enum errorcodes {ER_OK=0, ER_DOWNLOAD=3, ER_UPLOAD=4, ER_NOWIFI=1};

Operators, Specifiers and Pointers

Operators represent mathematical, relational, bitwise, conditional, or logical data manipulations. There are many operators in the C/C++ language. In this chapter, the most important are presented. Logical operators will be shown in the next chapter as they are used together with conditional statements.

Assignment Operator

  • Assignment operator ( = ) – the operator that assigns the value on the right to the variable on the assignment operator's left. The left side should represent the variable (must be able to be modified), and the right side can be the number (constant), variable, or expression. In the case of an expression, its value is first calculated, and then the result is assigned to the variable on the left. The work of an assignment operator can be seen in any of the following operation examples.

Arithmetic Operators

Arithmetic operations are used to do mathematical calculations with numbers or numerical variables. The arithmetic operators are the following.

  • Addition ( + ) – one of the four primary arithmetic operations used to add numbers. The addition operator can add only numbers, numeric variables, or a mix of both. The following example shows the use of the addition operator.
int result = 1 + 2; //The result of the addition operation will be 3
  • Subtraction ( - ) – the operation that subtracts one number from another where the result is the difference between these numbers.
int result = 3 - 2; //The result of the subtraction operation will be 1
  • Multiplication ( * ) – the operation that multiplies numbers and gives the result.
int result = 2 * 3; //The result of the multiplication operation will be 6
  • Division ( / ) – the operation that divides one number by another. If the result variable has the integer type, the result will always be the whole part of the division result without the fraction behind it. If the precise division is necessary, using the float type of variable for this purpose is important.
//The result of the division operation will be 3 
//(Only the whole part of the division result)
int result = 7 / 2;        
//The result of the division operation will be 3.5
float result2 = 7.0 / 2.0; 
  • Modulo ( % ) – the operation that finds the remainder of the division of two numbers.
//The result of the modulo operation will be 1, 
//Because if 7 is divided by 3, the remaining is 1
int result = 7 % 3; 

Bitwise Operators

Bitwise operators perform operations on bits in the variable. Among them, there exist bitwise logic operations. It means the same logic function is applied to every pair of bits in two arguments. Bitwise or ( | ) means that if at least one bit is “1” at the chosen bit position, the resulting bit will also be “1”. Bitwise and ( & ) means that if at least one bit is “0”, the resulting bit is “0”. Bitwise operators shouldn't be confused with Logic Operators ( || ), ( && ), which operate on a single boolean logic value.

byte result = 5 | 8;
; //The operation in numbers gives the result of 13
; //in bits can be shown as follows
; // 00000101b
; // 00001000b
; // ---------
; // 00001101b
byte result = 5 & 1;
; //The operation in numbers gives the result of 1
; //in bits can be shown as follows
; // 00000101b
; // 00000001b
; // ---------
; // 00000001b

Bitwise operators also allow shifting data left ( « ) or right ( » ) chosen number of bit positions. Shifting is often used in embedded programming to access the bit at a specific position. Shifting data one bit left gives the result of multiplication by 2 while shifting one bit right gives the effect of dividing by 2.

byte result = 5 << 1;
; //The operation in numbers gives the result of 10
; //in bits can be shown as follows
; // 00000101b
; // 00001010b

Compound Operators

Compound operators in C/C++ are a short way of writing down the arithmetic operations with variables. All of these operations are done on integer variables. These operands are often used in the loops when it is necessary to manipulate the same variable in each cycle iteration. The compound operators are the following.

  • Increment ( ++ ) – increases the value of integer variable by one.
int a = 5;
a++; //The operation a = a + 1; the result will be 6
  • Decrement ( - - ) – decreases the value of the integer variable by one.
int a = 5;
a--; //The operation a = a – 1; the result will be 4
  • Compound addition ( += ) – adds the right operand to the left operand and assigns the result to the left operand.
int a = 5;
a+=2; //The operation a = a + 2; the result will be 7
  • Compound subtraction ( -= ) – subtracts the right operand from the left operand and assigns the result to the left operand.
int a = 5;
a-=3; //The operation a = a – 3; the result will be 2
  • Compound multiplication ( *= ) – multiplies the left operand by the right operand and assigns the result to the left operand.
int a = 5;
a*=3; //The operation a = a × 3; the result will be 15
  • Compound division ( /= ) – divides the left operand with the right operand and assigns the result to the left operand.
int a = 6;
a/=3; //The operation a = a / 3; the result will be 2
  • Compound modulo ( %= ) – takes modulus using two operands and assigns the result to the left operand.
int a = 5;
//The result will be the remaining 
//Part of the operation a/2; it results in 1
a%=2; 
  • Compound bitwise OR ( |= ) – bitwise OR operator that assigns the value to the operand on the left.
int a = 5;
a|=2; //The operation a=a|2; the result will be 7
  • Compound bitwise AND ( &= ) – bitwise AND operator that assigns the value to the operand on the left.
int a = 6;
a&=; //The operation a=a&2; the result will be 2

& and * Operators: Pointers and References

Simple and complex types can be referred to with the use of pointer variables. A pointer is a variable that holds the address of the variable. The length of the pointer is equivalent to the length of the memory address (usually 16, 32 or 64 bits). A pointer does not contain a value but rather points to the variable (a memory) where the value is stored. A pointer variable must be initialised and dereferenced with Address-Of and Dereferencing operators.
The following example presents a simple type declaration and the use of a pointer variable.

& operator returns an address of a variable.
* operator dereferences a variable (it provides access to a value that the pointer variable points to).

int n = 10;     //Declare a variable of type int and initialize it with 10
int *ptr;       //Declare a pointer variable.
                //At this point, *ptr does not contain any address yet, 
                //rather some random address or null.
ptr = &n;       //Assign to the pointer ptr an address of the variable n
                //ptr contains now an address of the memory where variable n is located, 
                //not a value 10
int k;          //Declare another variable
k = *ptr;       //Assign k a value that is pointed by ptr

Simple type variables such as int, double, float and so on are passed to the function arguments as values, so the original value is copied, and a copy is presented to the function code (more on functions one can find in the Sub-programs, Functions). Modifications to the argument do not change the original value but just a copy. This is not the case when passing a complex type, such as an array, as an argument. The importance of pointers is not to be underestimated in this case: you simply declare a pointer pointing to the array's first element and pass it to the function. Then modifying the pointer value (an address), it is possible to refer to the following elements of the array. In this case, any modification to the referred array element modifies an original one, so the change in the value is instant. It does not need a return variable from a function.

Program Control Statements, Logical operators

It is essential to understand that if no statements change the normal program flow, the microcontroller executes instructions one by one in the order they appear in the source code (from the top - to the down direction). Control statements modify normal program flow by skipping or repeating parts of the code. Often to decide if the part of the code should be executed or to choose one of the number of possible execution paths, conditional statements are used. For repeating the part of the code, loop statements can be used.

Conditional Statement

if is a statement that checks the condition and executes the following statement if the condition is TRUE. There are multiple ways how to write down the if statement:

//1st example
if (condition) statement;     
 
//2nd example
if (condition)
statement;                    
 
//3rd example
if (condition) { statement; } 
 
//4th example
if (condition)
{
  statement;
}                             

The version with curly braces is used when there is a need to execute part of the code that consists of more than a single statement. Many statements taken together with pair of curly braces are treated as a single statement in such cases. When both TRUE and FALSE cases of the condition should be viewed, the else part should be added to the if statement in the following ways:

if (condition) {
  statement1;    //Executes when the condition is true
}
else {
  statement2;    //Executes when the condition is false
}

If more conditions should be viewed, the else if part is added to the if statement:

if (condition1) {
  statement1;    //Executes when the condition1 is true
}
else if (condition2) {
  statement2;    //Executes when the condition2 is true
}
else {
  statement3;    //Executes in all other cases
}

For example, when the x variable is compared and when it is higher than 10, the digitalWrite() method executes.

if (x>10) 
{
  //Statement is executed if the x > 10 expression is true
  digitalWrite(LEDpin, HIGH)  
}

Logical Operators

To allow checking different conditions, logical operators are widely used with the condition statement if described above.

Comparison Operators

There are multiple comparison operators used for comparing variables and values. All of these operators compare the variable's value on the left to the value on the right. Comparison operators are the following:

  • == (equal to) – if they are equal, the result is TRUE, otherwise FALSE;
  • != (not equal to) – if they are not equal, the result is TRUE, otherwise FALSE;
  • < (less than) – if the value of the variable on the left is less than the value of the variable on the right, the result is TRUE, otherwise FALSE;
  • < = (less than or equal to) – if the value of the variable on the left is less than or equal to the value of the variable on the right, the result is TRUE, otherwise FALSE;
  • > (greater than) – if the value of the variable on the left is greater than the value of the variable on the right, the result is TRUE, otherwise FALSE;
  • > = (greater than or equal to) – if the value of the variable on the left is greater than or equal to the value of the variable on the right, the result is TRUE, otherwise FALSE.

Examples:

if (x==y){ //Equal
  //Statement
}
 
if (x!=y){ //Not equal
  //Statement
}
 
if (x<y){ //Less than
  //Statement
}
 
if (x<=y){ //Less than or equal
  //statement
}
 
if (x>y){ //Greater than
  //Statement
}
 
if (x>=y){ //Greater than or equal
  //Statement
}

Boolean Operators

The Boolean logical operators in C/C++ are the following:

  • ! (logical NOT) – reverses the logical state of the operand. If a condition is TRUE the logical NOT operator will turn it to FALSE and the other way around;
  • && (logical AND) – the result is TRUE when both operands on the operator's left and right are TRUE. If even one of them is FALSE the result is FALSE;
  • || (logical OR) – the result is TRUE when at least one of the operands on the left and on the right of the operator is TRUE. If both of them are FALSE, the result is FALSE.

Examples:

//Logical NOT
if (!a) { //The statement inside if will execute when the a is FALSE
  b = !a; //The reverse logical value of a is assigned to the variable b
}
 
//Logical AND
//The statement inside if will execute when the 
//Values both of the a and b are TRUE
if (a && b){  
  //Statement
}
 
//Logical OR
//The statement inside if will execute when at least one of the 
//a and b values are TRUE
if (a || b){  
  //Statement
}

Switch Case Statement

A switch statement similar to the if statement controls the flow of a program. The code inside switch is executed in various conditions. A switch statement compares the values of a variable to the specified values in the case statements. Allowed data types of the variable are int and char. The break keyword exits the switch statement.

Examples:

switch (x) { 
   case 0:  //Executes when the value of x is 0
   // statements
   break;   //Goes out of the switch statement
 
   case 1:  //Executes when the value of x is 1
   // statements
   break;   //Goes out of the switch statement
 
   default:  //Executes when none of the cases above is true
   // statements
   break;   //Goes out of the switch statement
}

Check Yourself

1. Which code part is the correct one?

  • if(value == 1) digitalWrite(13, HIGH)
  • if (value == 1); digitalWrite(13, HIGH)
  • if (value == 1) DigitalRead(13,1)

2. What is the output of the next code part?

int x = 0;
 
    switch(x)
    {
 
      case 1: cout << "One";
 
      case 0: cout << "Two";
 
      case 2: cout << "Hello, world!";
 
    }
 

3. In which case should theswitch structure be used?

Loops

Loops are critical to control flow structures in programming. They allow executing statements or some part of the program repeatedly to process elements of data tables and texts, making iterative calculations and data analysis. In the world of microcontrollers, where sometimes there is no operating system, the whole software works in the main loop called a super loop. It means the program never ends and works until the power is off. This is clearly visible in the Arduino programming model, with one part of the code executed once after power-on setup() , and another executed repeatedly loop() . In C/C++, there are three loop statements shown in this chapter.

for

for is a loop statement that allows specifying the number of times of execution statements inside it. Each time all statements in the loop's body are executed is called an iteration. In this way, the loop is one of the basic programming techniques used for all programs and automation in general.

The construction of a for loop is the following:

for (initialization ; condition ; operation with the cycle variable) {
  //The body of the loop
}

Three parts of the for construction are the following:

  • initialisation section usually initialises the value of the cycle variable that will be used to iterate the loop; the initialisation value is ofter 0 but can be any other value;
  • condition allows managing the number of loop iterations; the statements in the body of the loop are executed when the condition is TRUE;
  • operation with the cycle variable specifies how the cycle variable is modified every iteration (incremented, decremented); allows defining the number of loop iterations.

The example of the for loop:

for (int i = 0; i < 4; i = i + 1) 
{
    digitalWrite(13, HIGH); 		
    delay(1000);			
    digitalWrite(13, LOW);			
    delay(1000);	
}

On the initialisation of the for loop, the cycle variable i = 0 is defined. The condition states that the for loop will be executed while the variable i value will be less than 4 (i < 4). In operation with the cycle variable, it is increased by 1 each time when the loop is repeated.

In the example above, Arduino function digitalWrite is used. It sets the logical state high or low at the chosen pin. If a LED is connected to pin 13 of the Arduino board, it will turn on/off four times.

while

while loop statement is similar to the for statement but does not contain the cycle variable. Because of this, the while loop allows executing a previously unknown number of iterations. The loop management is realised using only condition that needs to be TRUE for the next cycle to execute.

The construction of the while loop is the following:

while (condition is TRUE)
{
  //The body of the loop
}

That way, the while loop can be used as a good instrument for the execution of a previously unpredictable program. For example, if it is necessary to wait until the signal from pin 2 reaches the defined voltage level = 100, the following code can be used:

int inputVariable = analogRead(2);
while (inputVariable < 100)
{
    digitalWrite(13, HIGH); 		
    delay(10);			
    digitalWrite(13, LOW);			
    delay(10);	
    inputVariable = analogRead(2);
}

In the loop above, the LED that is connected to pin 13 of the Arduino board will be turned on/off until the signal reaches the specified level.

do...while

The do…while loop works similarly to the while loop. The difference is that in the while loop, the condition is checked before entering the loop, but in the do…while, the condition is checked after the execution of the statements in the loop, and then if the condition is TRUE the loop repeats. As a result, the statements inside the loop will execute at least once, even if the test condition is FALSE.

The construction of a do while loop is the following:

do {
  //The body of the loop
} while (a condition that is TRUE);

If the same code is taken from the while loop example and used in the do…while loop, the difference is that the code will execute at least once, even if the inputVariable value is more than or equal to 100. The example code:

int inputVariable = analogRead(2);
do {
    digitalWrite(13, HIGH); 		
    delay(10);			
    digitalWrite(13, LOW);			
    delay(10);	
    inputVariable = analogRead(2);
} while (inputVariable < 100);

Sub-programs, Functions

In many cases, the program grows to a size that becomes hardly manageable as a single unit. It is quite difficult to navigate through the code that occupies many screens. In such a situation, subprograms can help. Subprograms are named functions in C, and in C++, while they are associated with an object, they are named methods (in this chapter, the name function will be used). The function contains a set of statements that usually form some logical part of the code that can be separately tested and verified, making the whole program easy to manage. It is possible to group many functions by creating a library that is stored in a separate file. This is how external libraries are created.

Functions

Functions are the set of statements that are always executed when the function is called. A function can accept arguments as its input data and return the resulting value. Two functions from the Arduino programming model that were mentioned before are already known – setup() and loop(). The programmer usually tries to make several functions containing all the statements and then calls them in the setup() or loop() functions.

The structure of the function is following:

type functionName(arguments) //A return type, name, and arguments of the function
{
  //The body of a function – statements to execute
}

For example, a function that periodically turns on and off the LED can look like this:

void exampleFunction() 
{
  digitalWrite(13, HIGH); //the LED is ON		
  delay(1000);			
  digitalWrite(13, LOW);  //the LED is OFF				
  delay(1000);	
}

The example above shows that the return type of aexampleFunction function is void, which means the function does not return any value. This function also does not have any arguments because the brackets are empty.

This function should be called inside the loop() function in the following way:

void loop()
{
  exampleFunction(); //the call of the defined function inside loop()
}

The whole code in the Arduino environment looks like this:

void loop()
{
  exampleFunction(); //the call of the defined function inside loop()
}
 
void exampleFunction() 
{
  digitalWrite(13, HIGH); //the LED is ON		
  delay(1000);			
  digitalWrite(13, LOW);  //the LED is OFF				
  delay(1000);	
}

It can be seen that the function is defined outside the loop() or setup() functions.

When some specific result must be returned as a result of a function, then the function return type should be indicated, for example:

//the return type is "int"
int  sumOfTwoNumbers(int x, int y) 
{
    //the value next to the "return" should have the "int" type; 
    //this is what will be returned as a result.
    return (x+y); 
}

In the loop(), this function would be called in the following way:

void loop()
{
  //the call of the defined function inside the loop()
  int result = sumOfTwoNumbers(2, 3); 
}

Built-in functions

Every programming SDK, including Arduino IDE, comes with several ready-made functions that help develop applications, significantly reducing the effort and time of writing programs. These functions are written to handle inputs and outputs, process texts, communicate using serial ports, manipulate bits and bytes, and perform mathematical calculations. Refer to Arduino or other SDK documentation for details.

Library functions

The popularity of microcontrollers and embedded programming caused the growth of communities of enthusiasts who create a vast of useful software. This usually comes in the form of a set of functions created to handle some specific tasks, e.g. interfacing with a family of graphical displays or communicating using the chosen protocol. Functions created for one purpose are grouped together, forming the library. The number of libraries and their different version is so big that software developers use a special library manager to ensure that libraries are up-to-date or keep them in stable versions.

Function handlers

In the MCU world, is is common to use libraries that require a user (software developer) to implement a specific part of the code that is later automatically called by the library routines. Those functions are frequently called handlers and enable developers to inject their actions for a predefined set of activities without needing to modify library code. For this reason, the library contains a placeholder variable that can be assigned an executable code (a function body). Obviously, this is handled with the use of pointers. A sample function handler variable is presented in the following code, along with the user function definition, assignment to the handler variable and a call to the handler:

int (*hUserImplementedFunction)(int);       //Function handler variable 
                                            //(no code is here; it is just a pointer to the code, 
                                            //currently NULL, pointing to "nowhere"
...
int fMulx2(int a) {                         //User's implementation of the function. 
  return (2*a);                             //Multiply the argument 'a' value by 2 and return it to the callee.
  }                                         //Note: argument types and return types must match with the variable above
...
hUserImplementedFunction = fMulx2;          //assign a function to the handler
                                            //starting from now, hUserImplementedFunction contains an address of the 
                                            //fMulx2 function
...
int j;                                      
if (hUserImplementedFunction!=NULL)         //check if the handler is not null to avoid NULL pointer exception and code hang
    j = hUserImplementedFunction(10);        //call a handler, j is 20 now
In the example above, there is no “&” (address of) operator used when assigning a function code to the handler that is a pointer. It is because, by default, complex types as functions are referenced by reference (a pointer), not by value: fMulx2 simply represents an address where the code starts.
Using function handler is common for asynchronous actions, where user code is notified by the handler (usually low-level library code about the action to happen, i.e. data has been sent via the network interface). This method is similar to interrupts, as described later. Using function pointers (handlers) enables code to modify routines handling actions dynamically just by substituting the addresses. Libraries frequently implement handler variables as lists or arrays instead of singular values, enabling adding more than one action (handler) to be called by the library.

Structures and Classes

Structures and classes present complex data types, definable by the developer. Not all C/C++ programming environments provide support for classes (i.e., STM32 in HAL framework mode does not), but luckily, the Arduino framework supports it. Structures, conversely, are part of the C language definition and are present in almost every implementation of software frameworks for IoT microcontrollers.

Structures

In C and C++, a structure is a user-defined data type that allows you to combine different types of variables under a single name. A structure primarily groups related variables, forming a complex data type. A custom data structure (type) that can hold multiple variables, each with its own data type. These variables, called members or fields, can be of any built-in or user-defined type, including other structures. The sample named structure (equivalent to the complex type), variable declaration and use of member fields are presented below:

struct address {
  String city;
  String PO;
  String street;
  double longitude;
  double latitude;
};
...
address adr1;

Note it is also possible to declare a structure variable directly without defining a type:

struct {
  String city;
  String PO;
  String street;
  double longitude;
  double latitude;
} adr2, adr3;

Structures with type definitions are common when authoring libraries to let library users be able to declare new variables on their own, simply using a type.

Manipulating Structure's Data

Access to the fields of the structure's member variables (short: members, fields) is possible using the “.” (dot) operator.

adr1.city = "Gliwice";
adr2.city = "Oslo";
adr3.street = "Rodney";

The structure's data can be initialised member by a member or at once using the simplified syntax. Order is meaningful, and types need to fit the definition (C++ only):

adr3 = {"Liverpool", "L1 9EX", "27 Rodney", -2.973083901947872, 53.401615049766406 };

In C++, structures can also have member functions that manipulate the data (in C, they cannot). That is not so far from the Classes idea described in the following chapter. In the case of using C (or poor implementation of C++ that does not support classes nor member functions, i.e. STM32), it is common to prepare a set of data handling functions that operate on the structure referenced with a pointer. A common rule of thumb is the structure is the first argument in the function:

struct calcdata
{ 
  double x,y;
} args;
 
//Adds x and y of the "arguments" structure
double fCalcDataAdd(calcdata *arguments){
  return (arguments->x + arguments->y);
}
//Multilies x and y of the "arguments" structure
double fCalcDataMul(calcdata *arguments){
  return ((arguments->x)*(arguments->y));
}
//Sets x and y of the "arguments" structure
void fCalcDataSet(calcdata *arguments, double px, double py){
  arguments->x = px;
  arguments->y = py;
}

In the examples above, we use a “→” dereference operator to access the member fields by using the pointer to the structure rather than the structure itself.

Sample use of the functions is then:

args = {2,7};                //initialise structure x=2, y=7
fCalcDataSet(&args, 12,12);  //reinitialise structure x=12, y=12
int z = fCalcDataAdd(&args); //z equals to 24 now

Classes

Classes were introduced in C++ to extend structures encapsulating data and methods (functions) to process this data. A method presented above in the structure context brings an overhead with a need to pass a pointer to the structure for each call. Moreover, it makes access levels tricky, i.e. when you do not want to expose some functions but rather use them for internal data processing. Thus classes can be considered as an extension of the structures.

Classes are an important part of IoT programming as they bring an idea of the digital twin to low-level programming: a class used to represent a single piece of hardware, i.e. a sensor and provide its current state, access to the data it grabs and gives access to the operations on the hardware. Thus, in most IoT projects, each device external to the MCU that composes an IoT device is represented by one or more classes on the software side.
Classes are legit only for C++ and not in regular C implementations.

Sample class definition is presented below:

class Calculator
{
  public: //you can access this part
  int x,y;
    Calculator() {  //Default constructor
      clear();
    }
    Calculator(int px, int py) { //Another constructor
      x=px; 
      y=py;
    }
    ~Calculator(){} //This is dummy destructor
    int Add(){ return x+y; }
    int Mul(){ return x*y; }
    void setX(int px){ x=px; }
    void setY(int py){ y=py; }
  private: //that part is private, and you cannot access it      
    void clear(){
      x=0; y=0;
    }
};

The code above declares a new type, Calculator, with member fields (members in short) x and y and methods (functions) Calculator, Add, Mul, setX and setY. Some are marked as private: and accessible only from the code of the functions (methods) within the class; some are exposed to external users when marked as public:.
Constructors
There are “special” functions whose name is equivalent to the class name in this example above. Those are called constructors and are executed once the object of the class type is instantiated:

Calculator calc1=Calculator(2,15);

The above code instantiates an object calc1 of the class Calculator and calls the constructor explicitly Calculator(int px, int py). The other constructor, Calculator(), is the default one, and if not explicitly called by the code developer, it is automatically called when the object is instantiated.
There can be multiple constructors, and the one executed is selected based on the arguments set.
Destructor
A destructor is called automatically when an object's lifetime is to end. It allows, i.e. to release resources, disconnect open connections, and, in general, do some cleanup before the object is gone. The destructor function in the example above does nothing and is not obligatory in the code. Destructor name starts with a ~ sign (tilde) and has the same name as a class (or constructor):

~Calculator(){} //This is dummy destructor
In the embedded world, explicitly implemented destructors releasing allocated memory are rare as for the safety of the software, dynamic allocation of the memory is rather to be avoided; thus, destructors are eventually related to the network connections more than memory management.

Members
Member fields can be of any type. When marked as private they are accessible only from the code of the constructors, destructor and methods within the class. When public, one can reference them using a “.” (dot) operator, as in the case of the structures. When using a pointer to the class instance (object) rather than an instance itself (quite common), a “→” operator works as in the case of structures.
Methods
A method can have any name other than reserved (i.e. for constructors and destructor). Methods marked as public are available for the object user and are referenced similarly to member fields (“.” and “→” operators). private methods are not exposed externally; their purpose is to be called from another method internally. Sample use of methods is presented below:

  //continuing initialisation above: calc1.x=2, calc1.y=15 
  int z = calc1.Add(); //z=17
  calc1.setX(10);      //calc1.x=10
  calc1.setY(20);      //calc1.y=20
  z = calc1.Mul();     //z=200

Class inheritance
Classes can be inherited. This mechanism enables the real power of C++, where existing models (classes) can be extended with new logic without a need to rewrite and fork existing source code. In the example above, the Calculator class misses some features, such as i.e. subtracting. A code below defines a new type BetterCalculator that inherits from the Calculator class, using “:” operator:

class BetterCalculator:public Calculator
{
  public:
    BetterCalculator() {
    }
    BetterCalculator(int px, int py):Calculator(px,py) {
    }
    int Sub(){return x-y;}
};

Members x and y are in the Calculator class. Inheritance before C++ release 11 requires explicit constructor definitions, as in the example above. We use public inheritance to give access to all public methods in the base Calculator class available from within the level of the BetterCalculator class. Note the public keyword in the class definition: class BetterCalculator:public Calculator.
Instantiation and use are similar to the presented ones in the previous examples:

  BetterCalculator calc2=BetterCalculator(10,6); //BetterCalculator->Calculator->x=10, y=6
  ...
  z = calc2.Sub(); //z=4
  z = calc2.Add(); //z=16 - you can use the underlying code in the Calculator 
                   //class without a need to rewrite it again

The description above does not deplete all features of C++ Object Oriented Programming. Please note, however, that in the case of the embedded C++, their implementation can be limited and may not contain all the features of the modern, standard C++ patterns.

A special note on the libraries with separate definition (header) and implementation (body)
Many libraries come with a class definition in the header file (.h) and its implementation in the code file (.cpp). This is convenient for separating use patterns and implementations. A special operator, “::” (double colon), is used in the implementation to refer the code to the definition in the header file.
The sample header file myclass.h with the aforementioned Calculator class is present below. It contains only the class definition but does not contain any implementing code.

#ifndef h_MYCLASS
#define h_MYCLASS
class Calculator{
  public: //you can access this part
  int x,y;
    Calculator();  //Default constructor
    Calculator(int px, int py); //Another constructor
    ~Calculator(); //This is dummy destructor
    int Add();
    int Mul();
    void setX(int px);
    void setY(int py);
  private: //that part is private, and you cannot access it      
    void clear();
};
#endif

The implementation code refers to the class definition in the header:

#include "myclass.h"
 
Calculator::Calculator() {}
Calculator::Calculator(int px, int py) { x=px; y=py; }
Calculator::~Calculator(){}
int Calculator::Add(){ return x+y;}
int Calculator::Mul(){ return x*y; }
void Calculator::setX(int px){ x=px; }
void Calculator::setY(int py){ y=py; }
void Calculator::clear(){ x=0; y=0; }

Timing

Writing code that handles interrupts that come from internal peripherals, for example, timers, is possible but depends strongly on the hardware. Because this chapter presents just an introduction to programming, some essential timing functions will be shown.

Delay

The simplest solution to make functions work for a particular time is to use the delay() [37] function. delay() function halts program execution for the time specified as the argument (in milliseconds).

The blinking LED code is a simple demonstration of delay functionality:

  digitalWrite(LED_BUILTIN, HIGH);   //Turn the LED on 
  delay(1000);                       //Stop program for a second
  digitalWrite(LED_BUILTIN, LOW);    //Turn the LED off 
  delay(1000);                       //Stop program for a second

Using delay() is convenient but has a severe drawback: the algorithm is halted, and only interrupts (or tasks in the background) are executed. The main algorithm is present in the figure 11. Some of the tasks, like, i.e. receiving serial transmissions, networking, and outputting set PWM values, continue to work as background tasks, using interrupts or task management (such as FreeRTOS).

Figure 11: Blocking call: use of the delay()

The alternative to using delay is to switch to the non-blocking method, based on timing with the use of millis() as presented below.

Millis

millis() [38] returns the number in milliseconds since MCU began running the current program. Note it has nothing to do with a real-time clock, as most microcontrollers and development boards simply do not have one. The readings are 32-bit and will roll over in approximately 49 days. millis() can be used to replace delay() but needs some additional coding. Instead of blocking the algorithm, here we check if the desired time has passed. Meanwhile, we can handle other tasks instead of blocking execution, as presented in the algorithm in figure 12.

Figure 12: Non-blocking call: use of the millis()

Here is an example code of blinking LED using millis(). Millis is used as a timer. Every new cycle time is calculated since the last LED state change. If the time passed is equal to or greater than the threshold value, the LED is switched:

//Unsigned long should be used to store time values as the millis() returns a 32-bit unsigned number
//Store value of current millis reading
unsigned long currentTime = 0; 
//Store value of time when last time the LED state was switched
unsigned long previousTime = 0; 
 
bool ledState = LOW; //Variable for setting LED state
 
const int stateChangeTime = 1000; //Time at which switch LED states
 
void setup() {
  pinMode (LED_BUILTIN, OUTPUT); //LED setup
}
 
void loop() {
  currentTime = millis(); //Read and store current time
 
  //Calculate passed time since the last state change
  //If more time has passed than stateChangeTime, change the state of the LED
  if (currentTime - previousTime >= stateChangeTime) { 
 
    previousTime = currentTime; //Store new LED state change time
    ledState = !ledState; //Change LED state to oposite
    digitalWrite(LED_BUILTIN, ledState); //Write current state to LED
  }
}

Sleep Modes

Some IoT-dedicated microcontrollers have special features such as sleep modes that hold program execution for a predefined time or unless an external trigger occurs. This can be used for periodic, time-based activities. Its side effect is energy efficiency. The model of this behaviour and its features are very vendor-specific and vary much: i.e. Espressif MCUs have the only option to restart the code. At the same time, STM32 can hold execution and then continue. Because of the variety of models, modes and features, we do not present here any specific solution but rather a general idea.

/data01/virt87891/domeenid/www.robolabor.ee/htdocs/homelab/data/pages/en/iot-open/introductiontoembeddedprogramming2/cppfundamentals/digital_IO.txt— MISSING PAGE — /data01/virt87891/domeenid/www.robolabor.ee/htdocs/homelab/data/pages/en/iot-open/introductiontoembeddedprogramming2/cppfundamentals/analog_IO.txt— MISSING PAGE —

Interrupts

Interrupt is a signal that stops the normal execution of a program in the processor and starts the function assigned to a specific source of it. Such a function usually is called Interrupt Service Routine (ISR) or interrupt handler. The ISR can be recognized as a task with higher priority than the main program. Interrupt signals can be generated by the external source, like a change of value on the pin, and by the internal source, like a timer or any other peripheral device. When the interrupt signal is received, the processor stops executing the code and starts the ISR. After completing the interrupt handler, the processor returns to the normal program execution state.

ISR should be as short as possible; good practice is avoiding delays and long code sequences. Suppose there is a need to trigger the execution of a long part of the code with an incoming interrupt signal. In that case, the good practice is to define the synchronization variable, modify this variable in the ISR with a single instruction, and handle all other steps in the main program. The interrupt handler does not have arguments and does not return any value, so its type is void. To ensure fast execution of the programs, some of the Arduino functions do not work or behave differently in the ISR; for example, the delay() function does not work inside the ISR. Variables used in the ISR must be declared as volatile.

Interrupts are used to detect important real-time events which occur during normal code execution of the code. ISR is executed only when there is a need to do it.

Polling vs. interrupts

Interrupts can help in efficient data transmission. Using interrupts and checking if some situation occurred periodically is unnecessary. Such continuous checking is named polling. For example, a serial port interrupt is executed only when new data comes without polling the incoming buffer in a loop. This approach saves the processor time and, in many situations, creates code that is more energy efficient.

Interrupt handling example

Because interrupts need support from the hardware layer of the microcontroller, the availability of specific interrupt sources depends heavily on the microcontroller model. For example, different Arduino models have different external interrupt pin availability. In most Arduino boards, pins numbered 2 and 3 can be used for interrupts; in Arduino Uno, only these two, while in ESP32 and STM32, almost any digital pin is valid.

Very often, interrupts are used together with hardware timers to generate stable frequency signals. It ensures accurate timing independent of the main loop content and delays. Because internal peripherals are very different for different microcontrollers in this chapter, the example for the external interrupt is shown.

The function attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) is called to attach an interrupt to the handler. This function has 3 arguments.

  1. pin – the pin number where the interrupt signal-generating device will be attached.
  2. ISR – the name of an ISR function.
  3. mode – defines when an interrupt signal is triggered. There are five basic mode values:
    • LOW – interrupt is triggered when the pin value is LOW,
    • HIGH – interrupt is triggered when the pin value is HIGH,
    • RISING – interrupt is triggered when the pin value is changed from LOW to HIGH,
    • FALLING – interrupt is triggered when the pin value is changed from HIGH to LOW,
    • CHANGE – interrupt is triggered when the pin value is changed in any direction.

The example program that uses external interrupt:

volatile bool button_toggle = 0; //A variable to pass the information from ISR to the main program
 
void setup() {
  //Define LED pin
  pinMode(13,OUTPUT);       
  //Define button pin
  pinMode(2,INPUT_PULLUP);  
  //Attach interrupt to button pin
  attachInterrupt(digitalPinToInterrupt(2),ButtonIRS,FALLING); 
}
 
void ButtonIRS() {  //IRS function
  button_toggle =!button_toggle;
}
 
void loop() {
  digitalWrite (13,button_toggle);
}

In this example, the code needed to handle the interrupt signal is just one instruction. Still, it shows how to use the synchronization variable to pass information from ISR to the main program keeping the ISR very short.

Programming patterns

This chapter presents some programming templates and fragments of the code that are common in embedded systems. Some of those patterns, such as i.e. non-blocking algorithms, which do not use delay(x) to hold program execution but use a timer-based approach instead. It has also been discussed in other chapters, such as in the context of timers Timing or interrupts Interrupts.

Tracing vs Debugging - Serial Ports

Almost any MCU has a hardware debugging capability. This technique is complex and usually requires an external debugger such as JTAG. Setting up hardware and software for simple projects may not be worth a penny; thus, the most frequent case is tracing over debugging. Tracing uses a technique where the Developer explicitly sends some data to the external device (usually a terminal, over a serial port, and eventually a display) that visualises it. The Developer then knows the variables' values and how the algorithm runs. The use of the serial port is common because this is the one that is most frequently used for programming. Thus, it can be used in reverse for tracing. For this reason, Arduino Framework implements a singleton object Serial present in every code. It is implemented by each Arduino Framework vendor at the level of the general library with Arduino Framework.
Note to use a Serial it is obligatory to initialise it with the use of the Serial.begin(x) method, providing the correct bps, where x is a transmission speed (rate) that suits the rate configured in the terminal. The most common rates are 9600 (default) and 115200, but other options are possible. On the terminal side, configuration is usually done in the menu or a configuration file, such as in the case of the platformio.ini file. Calling Serial.begin(x) is usually done as one of the first actions implemented in the Setup() function of the application code:

void setup(){
  delay(100);
  Serial.begin(115200);
  Serial.println();
  ...
}
A rule of thumb is that after programming and during a boot, every MCU drops some garbage to the serial buffer. That is visualised as several random characters in the terminal. To easily distinguish the tracing from the garbage, it is advised to put some delay(100) at the beginning of the code and drop one or two “new line” characters to scroll garbage up using dummy println() call (once or twice is usually enough).

The Serial object has a number of handy methods that can help represent various variable types in a textual form to be sent via a serial port to the terminal. The most common are:

  • Serial.print(x) where x is any simple type available in the Arduino Framework, such as integers and floats, but also visualises arrays of characters and String objects.
  • Serial.println(x) prints as above but additionally adds the end of line/newline character by the end of the transmission. Note that the Linux style is used in Arduino, so only ASCII 13 character is sent.

Interfacing with the Device - Serial Port

The serial port and a class Serial handling the communication are bi-directional. It means that you can send a message from MCU to the terminal and the opposite. This can be used as a simple user interface. All aforementioned configuration steps to ensure seamless cooperation of the MCU serial interface and terminal (application) are also in charge here. As data is streamed byte by byte, it is usually necessary to buffer it. Technically, the serial port notifies MCU every time a character comes to the serial port with the use of the interrupts. Luckily part of the job is done by the Serial class: all characters are buffered in an internal buffer, and one can check their availability using Serial.available(). This function returns the number of bytes received so far from the external device (here, i.e. a terminal) connected to the corresponding serial port.

Many MCUs provide hardware and software serial ports and allow to use more than one. However, one serial port is usually considered the main one, and it is used for programming (flashing) the MCU. It is also common that other ports are implemented as software ones, so they put extra load on the MCU's processor and resources such as RAM, timers and interrupt system.

Data in the serial port are sent as bytes; thus, it is up to the developer to handle the correct data conversion. Reading a single byte of the data is done using Serial.read(): it gets another character from the FIFO queue that stands behind the serial port software buffer. As most of the communication is done textual way, the Serial class has support to ease the reading of the strings: Serial.readString(), but use involves some extra logic such as the function may timeout. Also, it may contain the END-OF-LINE / NEXT-LINE characters that should be trimmed before use [39].

Hardware buttons

Hardware buttons tend to vibrate when switching. This physical effect causes bouncing of the state forth and back, generating, in fact, a number of pulses instead of a single edge during switching. Getting rid of this is called debouncing. In most cases, switches (buttons) short to 0 (GND) and use pull-up resistors, as in the figure 13.

 Sample circuit of the switch with an external pull-up resistor connected to the GPIO2 of the MCU
Figure 13: Sample circuit of the switch with an external pull-up resistor connected to the GPIO2 of the MCU

The switch, when open, results in VCC through R1 driving the GPIO2 (referenced as HIGH), and when short, 0 is connected to it, so it becomes LOW:

  • button short → GPIO2=LOW
  • button released → GPIO2=HIGH

Some MCUs offer internal pull-ups and pull-downs, configurable from the software level. The transition state between HIGH and LOW causes bouncing.

A dummy debouncing mechanism only checks periodically for a press/release of the button. The common period for debouncing is between 50ms and 200ms. The code below shows an example that has been provided for presentation purposes. Yet, it is not flexible nor pragmatic due to the exhausting use of the loop() function and extensive use of delay(). An internal pull-up resistor is in use in this example:

#define BUTTON_GPIO 2
 
bool bButtonPressed=false;
 
void setup() {
  Serial.begin(9600);
  pinMode(BUTTON_GPIO, INPUT_PULLUP);
}
 
void loop() {
  if (digitalRead(BUTTON_GPIO)==LOW && !bButtonPressed)
  {
    Serial.println("Button pressed");
    delay(200);
    bButtonPressed=true;
  }
  if (bButtonPressed && digitalRead(BUTTON_GPIO)==HIGH)
  {
    Serial.println("Button released");
    bButtonPressed=false;
    delay(200);
  }
}

A more advanced technique for complex handling of the buttons is presented below in the context of the State Machines.

Finite State Machine

A Finite State Machine (FSM) idea represents states and flow conditions between the states that reflect how the software is built for the selected system or its component. An example of button handling with the use of the FSM is present here. The FSM reflects the physical state of the device, sensor or system on the software level, becoming a digital twin of a real device.

For the simple case (without detecting double-click or long press), 3 different button states can be distinguished: released, debouncing and pushed. An enumerator is a good choice to model those states (it is easily expandable):

typedef enum {
  RELEASED = 0,
  DEBOUNCING,
  PRESSED
} tButtonState;

A flow between the states can be then described in the following diagram (figure 14).

 State machine and transitions for button handling with software debouncing
Figure 14: State machine and transitions for button handling with software debouncing
  • In the RELEASED state, there is waiting until the button is pressed (LOW, for the pull-up model). The time is noted when it occurs, and the state changes to the DEBOUNCING.
  • In the DEBOUNCING state, if debouncing time passes and the button is still pressed (LOW) machine changes its state to PRESSED. If the button in DEBOUNCING becomes released (HIGH), then the machine returns to the state RELEASED.
  • In the PRESSED state, it transits to the RELEASED whenever the button goes HIGH.

The state machine is implemented as a simple class and has 2 additional fields that store handlers for functions that are called when the state machine enters PRESSED or RELEASED. Those functions are called callbacks. There are 2 public functions for callback registration as callback handlers class members are private. fButtonAction() is intended to be called in a loop(), as many times as possible to “catch” all pushes of the button:

class PullUpButtonHandler{
  private:
    tButtonState buttonState=RELEASED;
    uint8_t ButtonPin;
    unsigned long tDebounceTime;
    unsigned long DTmr;
    void(*ButtonPressed)(void);       //On button pressed callback
    void(*ButtonReleased)(void);      //On button relased callback
    void btReleasedAction() {         //Action to be done when current state is RELEASED
      if(digitalRead(ButtonPin)==LOW) {
        buttonState = DEBOUNCING;
        DTmr = millis();
      }
    }
    void btDebouncingAction() {       //Action to be done when current state is DEBOUNCING
      if(millis()-DTmr > tDebounceTime)
        if(digitalRead(ButtonPin)==LOW) {
          buttonState = PRESSED;
          if(ButtonPressed!=NULL) ButtonPressed();
        }
        else
          buttonState=RELEASED;
    }
    void btPressedAction() {          //Action to be done when current state is PRESSED
      if(digitalRead(ButtonPin)==HIGH) {
        buttonState=RELEASED;
        if(ButtonReleased!=NULL) ButtonReleased();
      }
    }
  public:
    PullUpButtonHandler(uint8_t pButtonPin, unsigned long pDebounceTime) {  //Constructor
      ButtonPin = pButtonPin;
      tDebounceTime = pDebounceTime;
    }
    void fRegisterBtPressCalback(void (*Callback)()) {    //Function registering a On PRESSED callback
      ButtonPressed = Callback;
    }
    void fRegisterBtReleaseCalback(void (*Callback)()) {  //Function registering a On RELEASED callback
      ButtonReleased = Callback;
    }
    void fButtonAction()  //Main, non blocking loop. Handles state machine logic 
    {                     //along with private functions above
      switch(buttonState) {
        case RELEASED: btReleasedAction();
          break;
        case DEBOUNCING: btDebouncingAction();
          break;
        case PRESSED: btPressedAction();
          break;
        default:
          break;
      }
    }
};

Sample use looks as follows:

#define BUTTON_GPIO 2
 
PullUpButtonHandler bh = PullUpButtonHandler(BUTTON_GPIO, 200);
void onButtonPressed() {
  Serial.println("Button pressed");
}
void onButtonReleased() {
  Serial.println("Released");
}
void setup() {
  Serial.begin(9600);
  pinMode(BUTTON_GPIO, INPUT_PULLUP);
  bh.fRegisterBtPressCalback(onButtonPressed);
  bh.fRegisterBtReleaseCalback(onButtonReleased);
}
 
void loop() {
  bh.fButtonAction();
}
The PullUpButtonHandler is instantiated with a 200ms deboucing time. That defines a minimum press time to let the machine recognize the button press correctly. That time is quite long for most applications and use cases and can be easily shortened.

The great feature of this FSM is that it can be easily extended with new functions, such as i.e. detection of the double click or long button press.

Hardware-specific extensions in programming

Some generic programming techniques and patterns mentioned above require adaptation for different hardware platforms. It may occur whenever hardware-related aspects are in charge, i.e., accessing GPIOs, ADC conversion, timers, interrupts, multitasking (task scheduling and management), multicore management, power saving extensions and most of all, integrated communication capabilities (if any). It can be different for almost every single MCU or MCU family.
It is common for hardware vendors to provide rich examples, either in the form of documentation and downloadable samples (i.e. STM) or via Github (Espressif), presenting specific C/C++ code for microcontrollers.

Analog input

Some MCUs use specific setups. Analogue input may work out of the box. Still, low-level control usually brings better results and higher flexibility (i.e. instead of changing the input voltage to reflect the whole measurement range, you can regulate internal amplification and sensitivity.

A special note on analogue inputs in ESP32

Please note implementation varies even between the ESP32 chips family, and not all chips provide all of the functions, so it is essential to refer to the technical documentation [40].

ESP32 has 15 channels exposed (18 total) of the up to 12-bit resolution ADCs. Reading the raw data (12-bit resolution is the default, 8 samples per measure as default) using the analogRead() function is easy.
Technically, under the hood on the hardware level, there are two ADCs (ADC1 and ADC2). ADC 1 uses GPIOs 32 through 39. ADC2 GPIOs 0,2,4, 12-15 and 25-27. Note that ADC2 is used for WiFi, so you cannot use it when WiFi communication is enabled.
Just execute analogRead(GPIO).
Several useful functions are here (not limited to):

  • analogReadResolution(res) - where res is a value between 9 and 12 (default 12). For 9-bit resolution, you get 0..511 values; for 12-bit resolution, it is 0..4095 respectively.
  • analogSetCycles(ccl) - where ccl is number of cycles per ADC sample. The default is 8: the valid number is between 1 and 255.
  • analogSetClockDiv(divider) - sets base clock divider for the ADC. That has an impact on the speed of conversion.
  • analogSetAttenuation(a) and analogSetPinAttenuation(GPIO, a) - sets input attenuation (for all channels or selected channels). The default is ADC_11db. This parameter reflects the dynamic scaling of the input value:
    • ADC_0db - no attenuation (1V on input = 1088 reading on ADC), so full scale is 0..1.1V,
    • ADC_2_5db - 1.34 (1V on input = 2086 reading on ADC), so full scale is 0..1.5V,
    • ADC_6db - 1.5 (1V on input = 2975 reading on ADC), so full scale is 0..2.2V,
    • ADC_11db - 3.6 (1V on input = 3959 reading on ADC), so full scale is 0..3.9V.
Do not execute consequent way analogRead(). As technically all channels use the same two registers (ADC1 and ADC2), you need to give it some time to sample (i.e. delay(100) between consecutive reads on different channels).

Analog output

PWM frequently controls analogue-style, efficient voltage on the GPIO pin. Instead of using a resistance driver, PWM uses pulses to change the adequate power delivered to the actuator. It applies to motors, LEDs, bulbs, heaters and indirectly to the servos (but that works another way).

A special note on ESP32 MCUs

The classical analogWrite method, known from Arduino (Uno, Mega) and ESP8266, does not work for ESP32.
ESP32 has up to sixteen (0 to 15) PWM channels (controllers) that can be freely bound to any of the regular GPIOs.
The exact number of PWM channels depends on the family member of the ESP chips, i.e. ESP32-S2 and S3 series have only 8 independent PWM channels while ESP32-C3 has only 6. In the Arduino software framework for ESP32, it is referred to as ledc. ESP32 can use various resolutions of the PWM, from 1 to 20 bits, while regular Arduino uses only 8-bit one. Note - there is a strict relation between resolution and frequency: i.e. with high PWM frequency, you cannot go with a resolution too high as the internal frequency of the ESP32 chip is limited.

To use PWM in ESP32, one must perform the following steps:

  • configure GPIO pin as OUTPUT,
  • initiate PWM controller by fixing PWM frequency and resolution,
  • bind the controller to the GPIO pin,
  • write to the controller (not to the PIN!) providing a duty cycle related to the resolution selected above - every call persistently sets the PWM duty cycle until the next call to the function setting duty cycle.

More information and detailed references can be found in the technical documentation for the ESP32 chips family [41].

Sample code controlling an LED on GPIO 26 with 5kHz frequency and 8-bit resolution is presented below:

#include "Arduino.h"
 
...
 
#define RGBLED_R 26
#define PWM1_Ch   5
#define PWM_Res   8
#define PWM_Freq  5000
 
...
 
ledcSetup(PWM1_Ch, PWM_Freq, PWM_Res); //Instantiate timer-based PWM -> PWM channel
ledcAttachPin(RGBLED_R, PWM1_Ch); //Bind a PWM channel to the GPIO
ledcWrite(PWM1_Ch,255); //Full on: control via the PWM channel, not via the GPIO
...
You can bind one PWM channel to many GPIOs to control them synchronously.

This technique can be easily adapted to control, e.g. standard and digital servos. PWM signal specification to control servos is presented in the chapter hardware actuators.

Interrupts

Arduino boards used to have a limited set of GPIOs to trigger interrupts. In other MCUs, it is a rule of thumb that almost all GPIOs (but those used, i.e. for external SPI flash) can trigger an interrupt; thus, there is much higher flexibility in, i.e., the use of user interface devices such as buttons.

A special note on ESP8266 and ESP32

Suppose the interrupt routine (function handler) uses any variables or access flash memory. In that case, it is necessary to use some tagging of the ISR function because of the specific, low-level memory management. A use of IRAM_ATTR is necessary (part of the code present in Interrupts:

void IRAM_ATTR ButtonIRS() {  //IRS function
  button_toggle =!button_toggle;
}
If the ISR and some other process both write to the memory (variable), providing exclusive access to the variable is important. This may be achieved with so-called Muxes, Semaphores and critical sections to ensure no deadlock will occur. However, it is unnecessary if ISR writes to the variable and some other process is reading it. The use of volatile for the variable should be enough.
Without an advanced configuration, using the float type (hardware accelerated floating point) will cause the application to hang, throwing a panic error and immediate restart of the MCU. It is due to the specific construction of the MCU and FPU. Do not use the float type in interrupt handling. If floating point operations are needed, use double as this one is calculated the software way.

Timers

The number of hardware timers, their features, and specific configuration is per MCU. Even single MCU families have different numbers of timers, i.e., in the case of the STM32 chips, the ESP32, and many others. Those differences, unfortunately, also affect Arduino Framework as there is no uniform HAL (Hardware Abstraction Layer) for all MCUs so far.

A special note on ESP32 MCUs

The number of hardware timers varies between family members. Most ESP32s have 4, but ESP32-C3 has only two [42]. A timer is usually running at some high speed. The most common is 80MHz and requires a prescaller to be useful. Timers periodically call an interrupt (a handler) written by the developer and bound to the timer during the configuration. Because interrupt routines can run asynchronously to the main code and, most of all, because ESP32s (most) are double core, it is necessary to take care of the deadlocks that can appear during the parallel access to the shared memory values, such as service flags, counters etc.
Special techniques using the critical section, muxes and semaphores are needed when more than one routine writes to the shared variable between processes (usually main code and an interrupt handler). However, It is unnecessary in the scenario where the interrupt handler writes to the variable and some other code (i.e. in the loop() section reads it without writing, as in the case of the example presented below.
In this example, the base clock for the timer in the ESP32 chip is 80MHz, and the timer (tHBT - short from Hear Beat Timer) runs at the 1MHz speed (PRESCALLER is 80) and counts up to 2 000 000. So, the interrupt handler is effectively called once every 2 seconds. This code runs separate from the loop() function, asynchronously calling the onHBT() interrupt handler.
onHBT() interrupt handler swaps the boolean value every two seconds. The value then is translated by the main loop() code to drive an LED on the ESP32 development board (here it is GPIO 0), switching it on and off. The onHBT() handler function could directly drive the GPIO to turn the LED on and off. Still, we present a more complex example with a volatile variable LEDOn just for education purposes.

#include "esp32-hal-timer.h"
 
#define LED_GPIO 0      //RED LED on GPIO 0 - vendor-specific
#define PRESCALLER 80   //80MHz->1MHz
#define COUNTER 2000000 //2 million us = 2s
 
volatile bool LEDOn = false;
hw_timer_t *tHBT = NULL; //Heart Beat Timer
 
void IRAM_ATTR onHBT(){  //Heart Beat Timer interrupt handler
  LEDOn = !LEDOn;        //Change true to false and opposite; every call
}
 
void setup() {
  Serial.begin(9600);
  pinMode(LED_GPIO, OUTPUT);
 
  tHBT = timerBegin(0, PRESCALLER, true);  //Instantiate a timer 0 (first)
  // Most ESP32s (but ESP32-C3) have 4 timers (0-3), and ESP32-C3 has only two (0-1).
  if (tHBT==NULL) //Check timer is created OK, NULL otherwise
  {
    Serial.println("Timer creation error! Rebooting...");
    delay(1000);
    ESP.restart();
  }
  timerAttachInterrupt(tHBT, &onHBT, true); //Attach interrupt to the timer
  timerAlarmWrite(tHBT, COUNTER, true);     //Configure to run every 2s (2000000us) and repeat forever
  timerAlarmEnable(tHBT);
 
}
//Loop function only reads LEDOn value and updates GPIO accordingly
void loop() {
  digitalWrite(LED_GPIO, LEDOn);
}

Timers can also be used to implement a Watchdog. Regarding the example above, it is usually a “one-time” triggered action instead of a periodic one. All one needs to do is to change the last parameter of the timerAlarmWrite function from true to false.

Programming with the use of scripts

Several programming models for IoT script programming are available. Depending on the hardware model used (SoC or OS-based MCU), it may involve single script execution (e.g. Raspberry Pi Pico RP2040, Edge-class IoT) or multithreaded, parallel, multiple scripts, doing multiple tasks (e.g. Raspberry Pi 4, Fog-class IoT). The idea and model of the scripting programming for SoC class devices (edge) were presented in the chapter Script Programming with Middleware.
In the case of far more powerful, Fog-class IoT devices that are OS-based devices, a variety of programming languages and, thus, scripting interpreters are available.

Among others, the most common scripting languages for fog class devices are :

  • Bash scripting (OS command scripting) usually does not provide support for the GPIO, intended to automate OS tasks;
  • Python scripting, cross-platform for both Edge-class devices (Micrpython) and Fog-class (regular Python, usually run on Linux);
  • C#, limited to the Windows IoT for Raspberry Pi;

Bash scripting

As Bash scripting is well covered by many manuals for Linux, in the following chapters, we focus on two others: Python and C#. Moreover, accessing the GPIO in the case of the bash requires installing external tools; thus, it does not apply to IoT programming straightforwardly but rather as a supplementary tool to automate tasks other than core programming.

Python

Python programming for IoT devices is dual:

  • Python Fundamentals for IoT Regular Python interpreter can be used in Fog class devices such as Raspberry Pi and its clones. In this case, the Python interpreter is run as a separate process in the Linux OS, the same way as in regular PC computers. It has full access to the GPIO, however.
  • Micropython Micropython is dedicated to SoCs and is distributed as the firmware that must be flashed into the device. It is common that Micropython exposes serial communication on dedicated pins, exposing a Python console that looks similar to the command line Python interface in the PCs

C# .NET

csharpfundamentals When writing this publication, the .NET framework with C# interpreter is available only for Raspberry Pi devices as a part of the Windows IoT operating system [43].

[pczekalski]Add existing pages with C# and Python for RPI [pczekalski]Author a course on Micropython

Introduction to the Python programming for IoT


A program in Python is stored in text files on the device's file system, as Python's source code is interpreted, not compiled, opposite to C++. A typical file extension for programs in Python is .py. In the context of IoT programming, both Python and Micropython share the same syntax and mostly the same libraries, so source code, in many cases, is portable. General hardware-related libraries like GPIO handling or timers are shared between those two Python worlds, and hardware-specific differences are minor compared to the Arduino framework.
Python is simple and efficient in programming the not-so-complex IoT algorithms but does not offer the level of control needed in real-time applications. It can be easily used for prototyping, testing hardware and implementing simple tasks.

Obviously, Micropython does not contain nor allow the use of very complex libraries and frameworks that sometimes are provided to the developer with only binary backend (that is CPU or MCU specific) such as Tensorflow for AI applications.

Nowadays, Python interpreter usually comes with OS (Linux) preinstalled. The sample installation procedure for Raspbian OS is presented in the manual maintained by the Raspberry Pi manufacturer [44]. In the case of the popular Raspbian or Ubuntu for Raspberry Pi, there are usually 2 versions of Pythons preinstalled: Python 2 and Python 3, because of the historical differences between implementations. Many OS applications are written in Python.
Python version can be started from the terminal simply by calling:

~$ python --version
Python 3.8.10

In the case one needs to use a specific version, you can start the interpreter explicitly referring to Python 2 or Python 3:

~$ python2
Python 2.7.18 (default, Jul 29 2022, 09:29:52) 
[GCC 9.4.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> quit()
 
~$ python3
Python 3.8.10 (default, May 26 2023, 14:05:08) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> quit()

Python can be executed via a desktop graphical interface (in the graphical terminal), in a text-based Linux installation via terminal, or remotely via ssh. As it is possible to write applications with visual GUI, starting them in a non-graphical installation of the Linux OS will throw an error. To execute a Python script (program), one needs to execute the following:

~$ python mypythoniotapp.py

Linux, Windows and Mac systems used to bind a .py file extension with a default Python interpreter, so it is possible to run Python script directly, either with the use of file manager or execute it from the command line:

~$ ./mypythoniotapp.py
Note: Python script must be marked as “executable” to run it directly.

In the following chapters, we present Python coding elements that are specific to the microcontrollers. A full Python course is beyond the scope of this publication, but it can be easily obtained online (links presented by the end of the chapter).

IDEs for Python

A dozen of IDEs can be used to program in Python. The most common are:

  • IDLE Editor, formerly delivered with Raspbian OS in the bundle, requires GUI. It is currently obsolete but still popular among hobbyists.
  • Thonny Python IDE, which comes with Raspbian OS, recently took over IDLE.
  • Visual Studio Code with plugins for Python, common with Arduino framework and easily integrates remote Python development - it provides two development scenarios: local on the IoT device (Raspberry Pi, requires GUI) and remote from the PC to the IoT device, that works with headless Raspberry Pi OSes installations.
  • PyCharm Community Edition requires additional installation [45] and requires GUI.
  • Simple code can be authored in the terminal using any text editor (i.e. Nano), as Python source files do not require compilation and are plain text ones. This is not very convenient, but it can help if no dedicated IDE and GUI are available, e.g., for rapid work remotely.

The following subchapters present some IoT and embedded systems-specific Python programming and a very basic introduction:

Additional Resources for Python programming for beginners

For in-depth Python courses and more, follow the links:

  1. The Python syntax and semantics: Python Semantics.
  2. The Python Package Index (PyPi): PyPi.
  3. The Python Standard Library:PSL.
  4. Free online Python course: learnpython.org.

[pczekalski][✓ pczekalski, 2023-08-26] Port info from the old manuals subpages in the namespace

[pczekalski]Add a simple app in Python. Check if Micropython has printing support over serial for tracing

Python Data Types and Variables

Python aims to be consistent and straightforward in the design of its syntax. The best advantage of this language is that it can dynamically set the variable types depending on values types which are set for variables.

Base Types

Python has a wide range of data types, like many simple programming languages:

  • number,
  • string,
  • list,
  • tuple
  • dictionary.
Numbers

Standard Python methods are used to create the numbers:

var = 1234     #Creates Integer number assignment
var = 'George' #Creates String type var

Python can automatically convert types of the number from one type to another. Type can be also defined explicitly.

int a = 10
long a = 123L
float a = 12.34
complex a = 3.23J
<code>
 
==String==
To define Strings use eclosing characters in quotes. 
Python uses single quotes ', double " and triple """ to denote strings.
<code Python>
Name = "George'
lastName = "Smith"
message = """this is the string message which is spanning across multiple lines."""
List

List contains a series of values. To declare list variables uses brackets [].

A = [] #Blank list variable
B = [1, 2, 3] #List with 3 numbers
C = [1, 'aa', 3] #List with different types

List are zero-based indexed. Data can be assigned to a specific element of the list using an index into the list.

mylist[0] = 'sasa'
mylist[1] = 'wawa'
 
print mylist[1]

List aren't limited to a single dimension.

myTable = [[],[]]

In two-dimensional array the first number is always the rows number, when the second is the columns number.

Tuple

Python Tuples are defined as a group of values like a list and can be processed in similar ways. When assigned Tuples got the fixed size. In Python, the fixed size is immutable. The lists are dynamic and mutable. To define Tuples, parenthesis () must be used.

TestSet = ('Piotr', 'Jan', 'Adam')
Dictionary

To define the Dictionaries in the Python the lists of key–value pairs are used. This datatype is used to hold related information that can be associated through Keys. The Dictionary is used to extract a value based on the key name. Lists use the index numbers to access its members when dictionaries use a key. Dictionaries generally are used to sort, iterate and compare data.

To define the Dictionaries the braces ({}) are used with pairs separated by a comma (,) and the key values associated with a colon (:). Dictionaries Keys must be unique.

box_nbr = {'Alan': 111, 'John': 222}
box_nbr['Alan'] = 222     #Set the associated 'Alan' key to value 222'
print (box nbr['John'])   #Print the 'John' key value
box_nbr['Dave'] = 111     #Add a new key 'Dave' with value 111
print (box_nbr.keys())    #Print the keys list in the dictionary
print ('John' in box_nbr) #Check if 'John' is in the dictionary
                          #This returns true

All variables in Python hold references to objects, and are passed to functions. Function can't change the value of variable references in its body. The object's value may be changed in the called function with the “alias”.

>>> alist = ['a', 'b', 'c']
>>> def myfunc(al):
    al.append('x')
    print al

>>> myfunc(alist)
['a', 'b', 'c', 'x']
>>> alist
['a', 'b', 'c', 'x']

Python Program Control Structures

if Statements

If an expression returns TRUE statements are carried out. Otherwise they aren't.

if expression:
  statements

Sample:

no = 11
  if no >10:
   print ("Greater than 10")
   if no <=30
     printf ("Between 10 and 30")

Output:

>>>
Greater than 10
Between 10 and 30
>>>
else Statements

An else statement follows an if statement and contains code that is called when the if statement is FALSE.

x = 2
if x == 6
  printf ("Yes")
else:
  printf ("No")  
elif Statements

The elif (shortcut of else if) statement is used when changing if and else statements. A series of if…elif statements can have a final else block, which is called if none of the if or elif expression is TRUE.

num = 12
if num == 5:
  printf ("Number = 5")
elif num == 4:
  printf ("Number = 4")
elif num == 3:
  printf ("Number = 3")
else:
  printf ("Number = 12")

Output:

>>>
 Number = 12
>>>
Boolean Logic

Python uses logic operators like AND, OR and NOT.

The AND operator uses two arguments, and evaluates to TRUE if, and only if, both of the arguments are TRUE. Otherwise, it evaluates to FALSE.

>>> 1 == 1 and 2 == 2
True
>>> 1 == 1 and 2 == 3
False
>>> 1 != 1 and 2 == 2
False
>>> 4 < 2 and 2 > 6
False
>>>

Boolean operator or uses two arguments, and evaluates as TRUE if either (or both) of its arguments are TRUE, and FALSE if both arguments are FALSE.

The result of NOT TRUE is FALSE, and NOT FALSE goes to TRUE.

>>> not 2 == 2
False
>>> not 6 > 10
True
>>>
Operator Precedence

Operator Precedence uses mathematical idea of operation order, e.g. multiplication begin performed before addition.

>>> False == False or True
True
>>> False == (False or True)
False
>>> (False == False) or True
>>>True
>>>

Python Looping

while Loop

An if statement is run once if its condition evaluates to TRUE, and never if it evaluates to FALSE.

A while statement is similar, except that it can be run more than once. The statements inside it are repeatedly executed, as long as the condition holds. Once it evaluates to FALSE, the next section of code is executed.

i = 1
while i<=4:
  print (i)
  i+=1
print ('End') 

Output:

>>>
1
2
3
4
End
>>>

The infinite loop is a particular kind of the while loop, it never stops running. Its condition always remains TRUE.

while 1 == 1:
 print ('in the loop')

To end the while loop prematurely, the break statement can be used. When encountered inside a loop, the break statement causes the loop to finish immediately.

i = 0
while 1==1:
  print (i)
  i += 1
  if i >=3:
    print('breaking')
    break;
print ('finished')    

Output:

>>>
0
1
2
3
breaking
finished
>>>

Another statement that can be used within loops is continue.

Unlike break, continue jumps back to the top of the loop, rather than stopping it.

i = 0
while True:
  i+=1
  if i == 2:
    printf ('skipping 2')
    continue
  if i == 5:
    print ('breaking')
    break
  print (i)
print ('finished')  

Output:

>>>
1
skipping 2
3
4
breaking
finished
>>>
for Loop
n = 9
for i in range (1,5):
  ml = n * i
  print ("{} * {} = {}".format (n, i, ml))

Output:

>>>
9 * 1 = 9
9 * 2 = 18
9 * 3 = 27
9 * 4 = 36
>>>

Python Sub-Programs

Subprograms

One of the most important in mathematics concept is to use functions. Functions in computer languages implement mathematical functions. The executing function produces one or more results, which are dependent by the parameters passed to it.

In general, a function is a structuring element in the programming language which groups a set of statements so they can be called more than once in a program. Programming without functions will need to reuse code by copying it and changing its different context. Using functions enhances the comprehensibility and quality of the program. It also lowers the memory usage, development cost and maintenance of the software.

Different naming is used for functions in programming languages, e.g. as subroutines, procedures or methods.

Python language defines function by a def statement. The function syntax looks:

def function-name(Parameter list):
  statements, i.e. the function body

Function bodie can contain one or more return statement. It can be situated anywhere in the function body. A return statement ends the function execution and returns the result, i.e. to the caller. If the return statement does not contain expression, the value None is returned.

def Fahrenheit(T_in_celsius):
  """ returns the temperature in degrees Fahrenheit """
  return (T_in_celsius * 9 / 5) + 32
 
for t in (22.6, 25.8, 27.3, 29.8):
  print(t, ": ", fahrenheit(t))

Output:

>>>
22.6 :  72.68
25.8 :  78.44
27.3 :  81.14
29.8 :  85.64
>>>
Optional Parameters

Functions can be called with optional parameters, also named default parameters. If function is called without parameters the default values are used. The following code greets a person. If no person name is defined, it greets everybody:

def Hello(name="everybody"):
  """ Say hello to the person """
  print("Hello " + name + "!")
 
Hello("George")
Hello()

Output:

>>>
Hello George!
Hello everybody!
>>>
Docstrings

The string is usually the first statement in the function body, which can be accessed with function_name.doc. This is Docstring statement.

def Hello(name="everybody"):
  """ Say hello """
  print("Hello " + name + "!")
print("The docstring of the function Hello: " + Hello.__doc__)

Output:

>>>
The function Hello docstring:  Say hello
>>>
Keyword Parameters

The alternative way to make function calls is to use keyword parameters. The function definition stay unchanged.

def sumsub(a, b, c=0, d=0):
  return a - b + c - d
print(sumsub(12,4))
print(sumsub(42,15,d=10))

Only keyword parameters are valid, which are not used as positional arguments. If keyword parameters don't exist, the next call to the function will need all four arguments, even if the c needs just the default value:

print(sumsub(42,15,0,10))
Return Values

In above examples, the return statement exist in sumsub but not in Hello function. The return statement is not mandatory. If explicitly return statement doesn't exist in the sample code it will not show any result:

def no_return(x,y):
  c = x + y
res = no_return(4,5)
print(res)

Any result will not be displayed in:

>>>

Executing this script, the None will be printed. If a function doesn't contain expression the None will also be returned:

def empty_return(x,y):
    c = x + y
    return
res = empty_return(4,5)
print(res)

Otherwise the expression value following return will be returned. In this example 11 will be printed:

def return_sum(x,y):
  c = x + y
  return c
res = return_sum(6,5)
print(res)

Output:

>>>
9
>>>
Multiple Values Returning

Any function can return only one object. An object can be a numerical value – integer, float, list or a dictionary. To return i.e. three integer values, we can return a list or a tuple with these three integer values. It means that function can indirectly return multiple values. This following example calculates the Fibonacci boundary for a positive number, returns a 2-tuple. The Largest Fibonacci Number smaller than x is the first and the Smallest Fibonacci Number larger than x is next. The return value is stored via unpacking into the variables lub and sup:

def fib_intervall(x):
  """ returns the largest Fibonacci number, smaller than x and the lowest
  Fibonacci number, higher than x"""
  if x < 0:
    return -1
  (old, new, lub) = (0,1,0)
  while True:
    if new < x:
      lub = new 
      (old,new) = (new,old+new)
    else:
      return (lub, new)
 
while True:
  x = int(input("Your number: "))
  if x <= 0:
    break
  (lub, sup) = fib_intervall(x)
  print("Largest Fibonacci Number < than x: " + str(lub))
  print("Smallest Fibonacci Number > than x: " + str(sup))

Python for Hardware

Controlling GPIO

The following code presents a sample Python application that flashes an LED connected to Raspberry Pi's GPIO pin 5. One must build a circuit (LED + resistor of a proper value) and connect it to the GPIO before running the code.
This example uses a dedicated GPIO handling library (specific for hardware): RPi.GPIO. For other IoT platforms, this may vary, i.e. Micropython uses a Machine library instead that covers all microcontroller's hardware.

#Raspberry Pi Python sample code
 
import RPi.GPIO as GPIO  
import time
 
#Blinking function  
def blink(pin):  
GPIO.output(pin,GPIO.HIGH)  
time.sleep(1)  
GPIO.output(pin,GPIO.LOW)  
time.sleep(1)  
return  
 
#Use the Raspberry Pi GPIO pin numbers  
GPIO.setmode(GPIO.BOARD)  
#Set up GPIO output channel  
GPIO.setup(5, GPIO.OUT)  
#Blink GPIO5 5 times  
for i in range(0,5):  
blink(5)  
GPIO.cleanup()
Interrupts Handling

Similarly to the GPIO, interrupts are hardware-specific; thus, libraries may differ among platforms. The samples present below are for Raspberry Pi.
Following Python rules for working with signals and their handlers are listed below.

  1. A particular signal handler, once set, remains installed until it is explicitly reset (Python emulates the BSD style interface), except the SIGCHLD handler, which follows the underlying implementation.
  2. The critical section signal can't be “blocked” temporarily (all Unix flavours do not support it).
  3. Python signal handlers are called asynchronously as far as the Python user is concerned; they can only occur between the “atomic” instructions of the Python interpreter. Signals arriving during long calculations implemented in C (such as regular expression matches on large bodies of text) may be delayed for an arbitrary amount of time.
  4. When a signal arrives during an I/O operation, it is possible that the I/O operation raises an exception after the signal handler returns. It depends on the underlying Unix system’s semantics.
  5. The C signal handler always returns; it makes little sense to catch synchronous errors like SIGFPE or SIGSEGV.
  6. Python installs several signal handlers by default: SIGPIPE is ignored, and SIGINT is translated into a KeyboardInterrupt exception. All of them can be overridden.
  7. Some care must be taken if both signals and threads are used in the same program. When using signals and threads simultaneously, always perform signal() operations in the main thread of execution.
  8. Any thread can perform an alarm(), getsignal(), pause(), setitimer() or getitimer(); only the main thread can set a new signal handler, and the main thread will be the only one to receive signals. It means that signals can’t be used for inter-thread communication.
  9. Use locks instead.

The example program is shown below. It uses the alarm() function to limit the time spent waiting to open a file; very useful if the file needs to be transmitted over a serial device that may not be turned on, which would typically cause the os.open() to hang indefinitely. The best solution is to make a 5-second alarm before opening the file; if the operation takes too long, the alarm signal will be sent, and the handler will raise an exception.

#Raspberry Pi Python sample code
import RPi.GPIO as GPIO
import time
 
state = 0
 
GPIO.setmode(GPIO.BCM)
GPIO.setup(26, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(19, GPIO.IN, pull_up_down=GPIO.PUD_UP)
 
def interrupt_handler(channel):
  global state
 
  print("interrupt handler")
 
  if channel == 19:
    if state == 1:
      state = 0
      print("state reset by event on pin 19")
    elif channel == 26:
      if state == 0:
        state = 1
        print("state set by event on pin 26")
 
GPIO.add_event_detect(26, GPIO.RISING,
                      callback=interrupt_handler,
                      bouncetime=200)
GPIO.add_event_detect(19, GPIO.RISING,
                      callback=interrupt_handler,
                      bouncetime=200)
while (True):
  time.sleep(0)

/data01/virt87891/domeenid/www.robolabor.ee/htdocs/homelab/data/pages/en/iot-open/scriptingprogramming/micropythonfundamentals.txt— MISSING PAGE — /data01/virt87891/domeenid/www.robolabor.ee/htdocs/homelab/data/pages/en/iot-open/scriptingprogramming/csharpfundamentals.txt— MISSING PAGE —

Embedded communication

IoT systems and related data flows are typically structured into three basic layers 15, eventually into five 16, which is less popular and used mostly in advanced research [46] [47].
The lowest layer is the Perception (physical, acquisition) Layer, the intermediate is the Network Layer, and the higher is the Application Layer. The function of the Perception layer is to keep in contact with the physical environment. Devices working in this layer are designed as embedded systems with a network module. The modern embedded device includes a microcontroller, sensors, and actuators. External memories and other typical microcomputer peripherals are usually built into the microcontroller, so they do not require a special connection. Sensors are elements that convert a value of some physical parameter into an electrical signal, while actuators are elements that control environmental parameters. Sensors and actuators are interfaced with the microcontroller using different connection types, including simple digital or analogue connections or much more complex communication links and protocols. IoT nodes in the Perception layer communicate with higher layers using much more complex data transmission methods. The wire and wireless transmission protocols used between the Perception layer and other layers are described in communications_and_communicating_sut.

 IoT architecture, 3-layered
Figure 15: IoT architecture, 3-layered
 IoT architecture, 5-layered
Figure 16: IoT architecture, 5-layered

This chapter describes some popular internal protocols used to communicate between microcontrollers and other electronic elements called “embedded protocols”.

The embedded protocol that can be used in specific implementation depends mainly on the type of peripheral element. The method of connection and data exchange strictly depends on the kind of element. Some parts are analogue sensors that should be connected to an analogue-digital converter; some can be connected to digital pins working as inputs (for sensors) or outputs (for actuators).

Analog

Simple sensors do not implement the conversion and communication logic, and the output is just the analogue signal – voltage level, depending on the value of the measured parameter. It needs to be further converted into a digital representation; this process can be made by the Analogue to Digital Converters (ADC), implemented as the internal part of a microcontroller or separate integrated circuit. Examples of sensors with analogue output are a photoresistor, thermistor, potentiometer, and resistive touchscreen. ADC conversion is a process of conversion of the continuous-time signal into a discrete one. It has 2 crucial parameters to consider:

  • Sampling rate: usually measured in Hz (kHz, MHz) is a sampling frequency, or in other words, defines a time period between two consecutive reads. A Nyquist-Shannon theorem defines minimum sampling frequency. Oversampling (using higher than Nyquist-Shannon) is common because many ADC converters built into the MCUs tend to be noisy due to the electromagnetic inference of other components, such as e.g. built-in radio. Oversampling brings the capability to average consecutive reads and obtain more reliable and less noisy ADC conversion.
  • Sampling resolution: measured in bits, defines the minimum change in the input voltage that the device can measure, i.e. 12-bit resolution brings 4096 values that are mapped to the input range. Obviously, the ideal ADC converter maps the discrete values to the voltage input range in a linear way. Still, in real-life applications, input characteristics of the ADC used to be non-linear, and software correction may be required once input characteristics are evaluated.

It is worth noting to mention that each ADC has its useable input range (voltage), and the input and analogue signal should be altered accordingly. In real applications, input signal adaptation requires external electronics; thus, many ADC converters provide the ability to amplify the input signal, and it can be programmed.

Digital

Simple, true/false information can be processed via digital I/O. Most devices use positive logic, where, i.e. +5 V (TTL) or +3.3 V (the most popular, yet other voltage standards exist) presents a logical one, also referenced as HIGH. In contrast, 0V presents a logical zero, referenced as LOW. In real systems, this bounding is fuzzy. It brings some tolerance, simplifying, e.g. communication from 3.3 V output to 5 V input, without a need for the conversion (note, the reverse conversion is usually not so straightforward, as 3.3 V inputs driven by the 5V output may burn quickly). A sample sensor providing binary data is a button (On/Off).
Alternating HIGH and LOW constitutes a square wave signal, usually used as a clock signal (when symmetrical) or used to control the power delivered to the external devices with means of so-called PWM.

Communication protocols

Elements that need more data to be transferred (e.g. displays) usually use some digital data transmission protocol. It is often a serial protocol, meaning that data is transmitted bit by bit. Serial communication can be done in three modes.

  • In simplex mode, only one of the two devices on a link can transmit; the other can only receive. The simplex mode can use the entire capacity of the channel to send data.
  • In half-duplex mode, each station can transmit and receive, but not simultaneously. When one device sends, the other can only receive, and vice versa.
  • In full-duplex mode, both stations can transmit and receive simultaneously. The link must contain two physically separate transmission paths, one for sending and the other for receiving.

Serial data transmission can be done synchronously or asynchronously. In synchronous data transmission, bits are synchronized with a clock signal common to the transmitter and receiver. Examples of synchronous protocols are TWI (Two Wire Interface) and SPI (Serial Peripheral Interface). Asynchronous data transmission does not need any separate synchronization signal, but the transmitter and receiver must use the exact timings and synchronization information must be included in the information transmitted. Examples of asynchronous interfaces implemented in microcontrollers are 1-Wire and UART (Universal Asynchronous Receiver Transmitter).

PWM

The PWM signal controls the energy delivered to the device, usually a DC motor, LED light, bulb, etc. To control voltage, instead of using inefficient resistance-based voltage dividers (where the remaining part of the voltage is distracted as heat), PWM is based on approximating the energy delivered to the device with periodical switching on and off (HIGH and LOW). Only two voltages are delivered to the device: low (0V) and HIGH (Vcc, e.g. +5V). One can easily observe how PWM works, e.g. when dimming the LED, if recorded with a high fps camera: the LED light flashes with the PWM signal frequency.
PWM controls, in fact, the ratio between HIGH and LOW signals in one period: the higher the ratio, the more energy is being delivered to the device. It is called a duty cycle. A perfect square wave signal, usually referenced as a clock signal, has a duty cycle of 50% (or 0.5); thus, its energy is half of the energy that can be carried when the signal is HIGH all the time. An LED light with a duty cycle of 100% will be fully bright, and with a duty cycle of 0 will be off.

A 50% duty cycle does not necessarily transfer straightforwardly to 50% of brightness or 50% of maximum rpm of the DC motor rotation, as characteristics of the devices regarding the voltage and energy provided to their input may be non-linear.
Some devices are fragile to the changes and cannot accept instant on and off. For this reason, we can use a capacitor that acts as an intermediate energy accumulator and thus flattens the characteristics to be more linear.

PWM signal is then characterised by the following:

  • voltage (values when HIGH and LOW),
  • frequency,
  • duty cycle.

Generating PWM

In microcontrollers, PWM used to be generated with timers and interrupts to ensure asynchronous operation and stability of the operation. Due to the digital nature of the signal generation, a duty cycle generation precision is given by the PWM timer resolution. An 8-bit resolution splits a period into 256 chunks, and a single chunk defines the minimum time one can increment or decrement the duty cycle. Modern MCUs provide developers with much higher resolution, even up to 14-bit.

A frequency of 5kHz is equivalent to 0.2ms period that can be controlled in steps of 0.2/256 ms ~= 781 ns.
Sample visualisation of the 5kHz PWM signal (3.3V) is presented in the following figures, with a duty cycle of, respectively:

  • 50/256→~39us (19.5%) in image 17,
  • 100/256→~78us (28%) in image 18,
  • 150/256→~117us (58.6%) in image 19,
  • 200/256→~156us (78.1%) in image 20,
  • 250/255→~195us (98%) in image 21,.
 Visualisation of the 5kHz PWM signal with a duty cycle of 19.5%
Figure 17: Visualisation of the 5kHz PWM signal with a duty cycle of 19.5%
 Visualisation of the 5kHz PWM signal with a duty cycle of 28%
Figure 18: Visualisation of the 5kHz PWM signal with a duty cycle of 28%
 Visualisation of the 5kHz PWM signal with a duty cycle of 58.6%
Figure 19: Visualisation of the 5kHz PWM signal with a duty cycle of 58.6%
 Visualisation of the 5kHz PWM signal with a duty cycle of 78.1%
Figure 20: Visualisation of the 5kHz PWM signal with a duty cycle of 78.1%
 Visualisation of the 5kHz PWM signal with a duty cycle of 98%
Figure 21: Visualisation of the 5kHz PWM signal with a duty cycle of 98%
Because of the limited hardware resources, an increase in PWM generation resolution or PWM signal frequency may cause an inability to generate a signal or instability of the PWM generation process. Always refer to the MCU's hardware specification for details on the PWM signal limits.

A voltage delivered to the device powered with a PWM signal can be calculated as an integral of the PWM signal over time: e.g., a 50% duty cycle of the 5V signal is equivalent to the delivery of the constant 2.5V.

SPI

One of the most popular interfaces to connect different external devices is SPI (Serial Peripheral Interface). It is a synchronous serial interface and protocol that can transmit data with speeds up to 20 Mbps. SPI is used to communicate microcontrollers with one or more peripheral devices over short distances – usually internally in the device. High transmission speed enables SPI to be suitable even for sending animated video data to colourful displays. In SPI connection, there is always one master device, in most cases the microcontroller (μC) that controls the transmission, and one or more slave devices – peripherals. To communicate, SPI uses three lines common to all connected devices and one enabling line for every slave element.

Table 5: SPI Lines
Line Description Direction
MISO Master In Slave Out peripheral → μC
MOSI Master Out Slave In μC → peripheral
SCK Serial Clock μC → peripheral
SS Slave Select μC → peripheral

The MISO line is intended to send bits from slave to master, the MOSI wire transmits data from master to slave, and the SCK line sends clock pulses that synchronize data transmission. The master device always generates the clock signal. Every SPI-compatible device has the SS (Slave Select) input that enables communication in this specific device. Master is responsible for generating this enable signal – separately for every slave in the system.

 SPI connections
Figure 22: Sample SPI connection.

SPI is used in many electronic elements:

  • analogue to digital converters (ADC),
  • real-time clocks (RTC),
  • EEPROMs,
  • LCD, OLED, and e-paper displays,
  • communication interfaces (e.g. Ethernet, WiFi),
  • and many others.

Due to different hardware implementations, there are four SPI protocol operation modes. The mode used in the master must fit the mode implemented in the slave device.

Table 6: SPI Modes
Mode Clock polarity Clock phase Idle state Active state Output edge Data capture
mode 0 0 0 0 1 falling rising
mode 1 0 1 0 1 rising falling
mode 2 1 0 1 0 rising falling
mode 3 1 1 1 0 falling rising

It results in different timings of the clock signal concerning the data sent. Clock polarity = 0 means that the idle state of the SCK is 0, so every data bit is synchronised with the pulse of logic 1. Clock polarity = 1 reverses these states. Output edge (rising/falling) says at which edge of active SCK signal sender puts a bit on the data line. The data capture edge says at what edge of SCK signal data should be captured by the receiver.

 SPI timing
Figure 23: Sample SPI timing.

QSPI

Even if a 20MHz frequency ensures good transmission speed, it can be too slow for some use. Some modern microcontrollers use external flash memory for program storage and execute programs from internal SRAM memory, downloading executable code in chunks as required. This requires a higher data rate to avoid stalls in program execution. A QSPI (Quad-SPI) link was developed to achieve higher transmission speed. It has four bidirectional data lines instead of two unidirectional to increase speed four times. Additionally, it supports higher clock frequency, increasing speed even higher, currently more than 100MBps. Operation of QSPI requires a special protocol with a set of commands, so hardware implementation is much more complex than the original SPI.

 QSPI connections
Figure 24: Sample QSPI connection.

TWI (I2C)

TWI (Two Wire Interface) is one of embedded systems' most popular communication links and protocols. Philips has designed it as an I2C (Inter-Integrated Circuit) for audio-video appliances controlled by the microprocessor. Many chips can be connected to the processor with this interface, including:

  • EEPROM memory chips,
  • RAM memory chips,
  • AD/DA converters,
  • real-time clocks,
  • sensors (temperature, pressure, gas, air pollution),
  • port extenders,
  • displays,
  • specialised AV circuits.

TWI, as the name says, uses two wires for communication. One is the data line (SDA); the second is the clock line (SCL). Both lines are common to all circuits connected to the one TWI bus. The method of communication of TWI is the master-slave synchronous serial transmission. It means that data is sent bit after bit synchronised with the clock signal. The SCL line is always controlled by the master unit (usually the microcontroller); the signal on the SDA line is generated by the master or one of the slaves – depending on the direction of communication. The frequency rate of the transmission is up to 100 kHz for most of the chips; for some, it can be higher – up to 400 kHz. The new implementation allows an even higher frequency rate, reaching 5 MHz. At the output side of units, the lines have the open-collector or open-drain circuit. This means that external pullup resistors are needed to ensure the proper operation of the TWI bus. The value of these resistors depends on the number of connected elements, the speed of transmission, and the power supply voltage. It can be calculated with the formulas presented, e.g. in the Texas Instrument Application Report [48]. Usually, it is assumed between 1 kΩ and 4.7 kΩ.

 TWI bus connection
Figure 25: Sample TWI connection.

The data is sent using frames of bytes. Every frame begins with a sequence of signals called the start condition. Slaves detect this sequence, which causes them to collect the next eight bits that form the address byte – unique for every circuit on the bus. If one of the slaves recognises its address remains active until the end of the communication frame, others become inactive. To inform the master that some unit has been appropriately addressed slave responses with the acknowledge bit – it generates one bit of low level on the SDA line (the master generates clock pulse). After sending the proper address, data bytes are sent. The direction of the data bytes is controlled by the last bit of the address; for 0, data is transmitted by the master (Write), and for 1, data is sent by the slave (Read). The receiving unit must acknowledge every full byte (eight bits). There is no limitation on the number of data bytes in the frame; for example, samples from the AD converter can be read continuously byte after byte. At the end of the frame, another special sequence is sent by the master–stop condition. It is also possible to generate another start condition without the stop condition. It is called a repeated start condition.

 TWI frame
Figure 26: TWI frame.

Address byte only activates one chip on the bus, so every unit must have a unique physical address. This byte usually consists of three elements: a 4-bit field fixed by the producer. This 3-bit field can be set by connecting three pins of the chip to 0 (ground) or 1 (power supply line), a 1-bit field for setting the direction of communication (R/#W). Some elements (e.g. EEPROM memory chips) use the 3-bit field for internal addressing, so only one such circuit can be connected to one bus. There are no special rules for the data bytes. The first data byte sent by the master can be used to configure the slave chip. In memory units, it is used for setting the internal address of the memory for writing or reading in multi-channel AD converters to choose the analogue input. Detailed information on the meaning of every bit of the transmission is present in the documentation of the specific integrated circuit. The I2C standard also defines the multi-master mode, but in most small projects, there is one master device only.

1-Wire

1-Wire is a master-slave communication asynchronous bus interface designed formerly by Dallas Semiconductor Corp[49]. It can transmit data at long distances at the cost of transmission speed. The typical data speed of the 1-Wire interface is about 16.3 kbit/s, and the maximum length is approx. 300m. Name 1-Wire comes from the feature that the data line can directly power elements connected to the bus. A network chain of 1-Wire devices consists of one master device and many slave devices. Such a chain is called a MicroLAN. 1-Wire devices may be a part of a product's circuit board, a single component device such as a temperature probe, or a remote device for monitoring purposes.

Each 1-Wire device must contain a logic unit to operate on the bus. A dedicated bus converter is needed to connect a 1-wire bus to a PC. The most popular PC/1-Wire converters use a USB plug to connect to the PC and the RJ11 connectors (telephones 6P2C/6P4C modular plugs) for MicroLAN. 1-Wire devices can also be connected directly to the microcontroller boards.

1-Wire Protocol Description

Within the MicroLAN, there is always one master device, typically a PC or a microcontroller unit. The master always initiates activity on the bus to avoid collisions on the network chain. If a collision occurs, the master device retries the communication. In the 1-Wire network, many devices can share the same bus line. To identify devices in the MicroLAN, each connected device has a unique 64-bit ID number. The ID number's least significant byte defines the type of the device (temperature, voltage, etc.). The most significant byte represents a standard 8-bit CRC. The 1-Wire protocol description contains several broadcast commands and commands used to address the selected device. The master sends a selection command, then the address of the selected slave device. This way, the following command is executed only by the addressed device. The 1-Wire bus implements an enumeration procedure that allows the master to get information about the ID numbers of all connected slave devices to the MicroLAN network. The device address includes the device type, identifying what type of slaves are connected to the network chain. The 64-bit address space is searched as a binary tree. It makes it possible to find up to 75 devices per second.

The physical implementation of the 1-Wire network is based on an open drain master device connected to one or more open drain slaves. One single pull-up resistor for all devices pulls the bus up to 3/5 V and can be used to power the slave devices. 1-Wire communication starts when a master or slave sets the bus to low voltage (connects the pull-up resistor to ground through its output MOSFET).

 1-Wire bus
Figure 27: 1-Wire bus connection

The 1-Wire protocol allows for bursting the communication speed up by 10 factors. In this case, the master starts a transmission with a reset pulse, pulling down the data line to 0 volts for at least 480 µs. It resets all slave devices in the network chain bus. Then, any slave device shows it exists, generating the “presence” pulse. It holds the data line low for at least 60 µs after the master releases the bus. To send a “1”, the bus master sends a 1–15 µs low pulse. To send a “0”, the master sends a 60 µs low pulse. The negative edge of the pulse is used to start a slave's monostable multivibrator. The slave's multivibrator clocks to read the data bus about 30 µs after the falling edge. The slave's multivibrator has analogue tolerances that affect its timing accuracy, for the “0” pulses are 60 µs long, and “1” pulses are limited to a max of 15 µs. When the designed solution doesn't contain a dedicated 1-Wire interface peripheral, a UART can be used as a 1-Wire master. Dallas also offers Serial or USB “bridge” chips, which are very useful when the distance between devices is long (greater than 100 m). For longer, up to 300 m buses, the simple twisted pair telephone cable can be used. It will require adjustment of pull-up resistances from 5 kΩ to 1 kΩ. The basic sequence is a reset pulse followed by an 8-bit command, and after it, data can be sent/received in groups of 8-bits. In the case of transmission errors, the weak data protection 8-bit CRC checking procedure can be used.

To find the devices, the enumeration broadcast command must be sent by a master. The slave device responds with all ID bits to the master, and at the end, it returns a 0.

 1-Wire reset timings
Figure 28: 1-Wire reset timings
 1-Wire read timings
Figure 29: 1-Wire read timings
 1-Wire write timings
Figure 30: 1-Wire write timings

1-Wire Products

The Dallas/Maxim integrated 1-Wire devices list contains a wide range of implementations. The 1-Wire protocol can be quickly implemented into the current IoT boards; most manufacturers share the software libraries, allowing developers to include them in their projects in C, C++, and assembly languages. The 1-Wire sensors (temperature, humidity, pressure, etc.) are factory calibrated and reading the physical measurements follows the International System of Units (SI). 1-Wire products can be grouped as follows:

  • secure authenticators,
  • memory EPROM, EEPROM ROM,
  • temperature sensors and temperature switches,
  • data loggers,
  • 1-Wire interface products,
  • battery monitors, protectors, and selectors,
  • battery ID and authentication,
  • timekeeping and real-time clocks.

UART

UART name is an abbreviation of Universal Asynchronous Receiver Transmitter. It is one of the most often used communication methods, traditionally named serial interface or serial port. In contrast to previously presented interfaces, UART uses direct point-to-point communication. UART is the communication unit implemented in microcontrollers rather than the communication protocol. It sends the series of bits via the TxD pin and receives a stream of bits with the RxD pin. It is important to remember that pin TxD from one device should be connected to pin RxD in another device. This is a general rule, but please always check the documentation for some non-standard markings.

 UART connection
Figure 31: UART connection

The transmission speed and bit duration must be the same at the transmitter and receiver to properly transmit data. Although the transmission speed can be freely chosen, some standard, commonly used baud rates exist. They differ from 300 to 115200 bits per second. Higher baud rates are also available in modern microcontrollers, like 230400, 250000, 500000, 1M, 2M or 3Mbps. In UART, data is sent in frames. The frame begins with the start bit of value “zero”. Next, from five to eight data bits are transmitted. Next, an optional parity bit can appear. The frame is finished with the stop bit of value “one”. Stop bit can be prolonged to 1.5 or 2 times the standard bit duration. After at least one stop bit, the next frame can be sent, beginning with a start bit. Start and stop bits are used to synchronise the receiver and transmitter.

 UART frame
Figure 32: UART frame

UART, namely Serial Port, is used in many modern microcontrollers to upload the executable program, debug, and as the standard input/output for the user interface. For example, in Arduino, functions that operate on the serial port are included in a common set of built-in functions.

Many modern PC computers (except industrial ones) do not have a serial port exposed, so USB to serial converters must be used. Some development boards have a USB-serial converter on board (e.g. Arduino Uno, NodeMCU, STM Nucleo, etc.)
Even if a PC computer has a serial port, it is usually compatible with the RS-232 standard. It uses the same frame structure but different voltage levels (with opposite zero-one encoding, known as reverse logic).

IoT Hardware Overview

IoT hardware infrastructure is mainly inherited from the embedded systems of the SoC type for Edge class IoT devices and from PCs for Fog class. As IoT devices are by their nature network-enabled, many of the existing embedded platforms evolved towards network-enabled solutions, sometimes indirectly through delivering network communication module (wired or wireless) as an external device yet integrated on the development board (e.g. Arduino Uno with Ethernet Networking shield, GSM shield, etc.), sometimes a new system, integrating networking capabilities in one SoC (e.g. Espressif SoCs). More advanced devices that require OS to operate preliminarily benefited from externally connected peripheral network interfaces via standard wired ports like USB (i.e. early versions of the Raspberry Pi, where WiFi card was delivered as USB stick), currently, usually integrate most of the network interfaces in a single board (e.g. RPi 4, including Ethernet, WiFi and Bluetooth). Still, in the case of the Fog class devices, those are separate chips to the CPU, and they communicate over, e.g. PCI or ISB protocol.

A microcontroller with network capabilities is the key, but not the only element forming an IoT node device. Additional elements, including sensors and actuators, are needed to keep in touch with the environment.

In the following chapters, there is a description of the families of popular microcontrollers, sensors and actuators:

Finally, in the last sub-chapter, there is an introduction to the powering of IoT devices:

Most Noticeable Platforms

The IoT market is an emerging one. New hardware solutions appear almost daily, while others disappear quickly. At the moment of writing the first version of this book (2016-2019), some hardware solutions that seemed to be prominent for at least a couple of years existed. After a few years, while the 2nd edition of the publication is being prepared (2023–2025), most of the hardware solutions described previously are still present on the market, even strengthening their position and having modernized and improved versions (e.g. ESP32 as the successor of ESP8266). However, some other platforms increased their popularity, mainly because of their appearance in the VSCode programming environment with PlatformIO, and what is even more important, the possibility of writing programs in the Arduino model. In the following sections, a short review of these platforms is provided.

  • AVR: Arduino – a development board using the Atmel microcontroller, undoubtedly the most popular development platform for enthusiasts and professionals. Arduino itself barely offers networking capabilities yet; there is a vast number of extension boards, including network interfaces (both wired and wireless);
  • ESP: Espressif (Espressif Systems) – the great SoC solutions with wireless network interfaces built-in; the family of Espressif chips includes ESP8266 (WiFi) and ESP32 (WiFi, Bluetooth)
  • nRF52: Nordic Semiconductor SoC that is based on ARM architecture and offers NFC, Bluetooth and ZigBee capabilities
  • STM32: Another ARM-based family of SoCs; some of them have Bluetooth wireless module built-in
  • ARM: Raspberry Pi (and its clones) – advanced boards, including Linux operating system with GUI interface, even able to replace desktop computers.

The following chapters are dedicated to the families of the devices, describing their main features:

Sensors and Sensing

A sensor is an element that can turn a physical outer stimulus into an output signal, which can then be used for further analysis, management, or making decisions. People also use sensors like eyes, ears and skin to gain information about the outer world and act according to their aims and needs. Sensors can be divided into many categories according to the measured parameter of the environment.

 Environment sensing data flow
Figure 33: Environment sensing data flow

Usually, every natural phenomenon – temperature, weight, speed, etc. – needs specially customised sensors that can change phenomena into electric signals, usually the voltage, that microprocessors or other devices could use. Sensors can be divided into many groups according to the physical nature of their operations – touch, light, an electrical characteristic, proximity and distance, angle, environment and other sensors.

Touch Sensors

Button

A pushbutton is an electromechanical sensor that connects or disconnects two points in a circuit when force is applied. The button output discrete value is either HIGH or LOW.

 Pushbutton
Figure 34: Pushbutton

A microswitch, also called a miniature snap-action switch, is an electromechanical sensor that requires very little physical force and uses a tipping-point mechanism. Microswitch has three pins, two of which are connected by default. When the force is applied, the first connection breaks and one of the pins is connected to the third pin.

 Microswitch
Figure 35: Microswitch

The most common use of a pushbutton is as an input device. Both force solutions can be used as simple object detectors or as end switches in industrial devices. The button can be connected to any available digital pin that must be configured as input or input with a pullup. In the configuration presented in the figure below, a pull-up resistor is connected externally, so enabling it in the microcontroller is unnecessary.

 Schematics of Arduino Uno and a push button
Figure 36: Schematics of Arduino Uno and a push button

An example code:

int buttonPin = 2; //Initialization of a push button pin number
int buttonState = 0; //A variable for reading the push button status
 
void setup() {
  Serial.begin(9600);  //Begin serial communication
  pinMode(buttonPin, INPUT); //Initialize the push button pin as an input
}
 
void loop() {
  //Read the state of the pin where a button is connected
  //it is LOW if a button is pressed, HIGH otherwise
  //the buttonState variable holds the compliment state of the buttonPin
  buttonState = !digitalRead(buttonPin);
  //Check if the push button is pressed. If it is, the buttonState variable is HIGH
  if (buttonState == HIGH) { 
    //Print out text in the console
    Serial.println("The button state variable is HIGH - it is pressed."); 
  } else {
    Serial.println("The button state variable is LOW - it is not pressed.");
  }
  delay(10); //Delay in between reads for stability
}

Force Sensor

A force sensor predictably changes resistance depending on the applied force to its surface. Force-sensing resistors are manufactured in different shapes and sizes, and they can measure not only direct force but also tension, compression, torsion and other types of mechanical forces. Because the force sensor changes its resistance linearly, it should be connected to the analogue input. It is also required o connect another resistor to form the voltage divider, as shown in the figure below. The voltage is measured by the internal ADC of the microcontroller.

Force sensors are used as control buttons, object presence detectors, or to determine weight in electronic scales.

 Force sensitive resistor
Figure 37: Force sensitive resistor (FSR)
 Voltage is measured by applying and measuring constant voltage to the sensor
Figure 38: The voltage is measured by applying and measuring constant voltage to the sensor

An example code:

//Force Sensitive Resistor (FSR) is connected to the analogue 0 pin
int fsrPin = A0; 
//The analog reading from the FSR resistor divider
int fsrReading;      
 
void setup(void) {
  //Begin serial communication
  Serial.begin(9600);   
  //Initialize the FSR analogue pin as an input
  pinMode(fsrPin, INPUT); 
}
 
void loop(void) {
  //Read the resistance value of the FSR
  fsrReading = analogRead(fsrPin); 
  //Print 
  Serial.print("Analog reading = "); 
  Serial.println(fsrReading);
  delay(10);
}

Capacitive Sensor

Capacitive sensors are a range of sensors that use capacitance to measure changes in the surrounding environment. A capacitive sensor consists of a capacitor that is charged with a certain amount of current until the threshold voltage. A human finger, liquids or other conductive or dielectric materials that touch the sensor can influence the sensor's charge time and voltage level. Measuring charge time and voltage level gives information about changes in the environment. Ready-to-use sensors include an electronic element that performs measurements and returns digital information at the output so that they can be connected directly to a digital input pin.

Capacitive sensors are used as input devices and can measure proximity, humidity, fluid level and other physical parameters or serve as an input for electronic device control.

 Touch button module
Figure 39: Touch button module
 Arduino and capacitive sensor schematics
Figure 40: Arduino and capacitive sensor schematics
//Capacitive sensor is connected to the digital 2 pin
int touchPin = 2; 
 
//The variable that stores digital value read from the sensor
boolean touchReading = LOW; 
//The variable that stores the previous state of the sensor
boolean lastState = LOW; 
 
void setup() {
  //Begin serial communication
  Serial.begin(9600);  
  //Initialize the capacitive sensor analogue pin as an input
  pinMode(touchPin, INPUT);  
}
 
void loop() {
  //Read the digital value of the capacitive sensor
  touchReading = digitalRead(touchPin); 
  //If the new touch has appeared
  if (currentState == HIGH && lastState == LOW){ 
    Serial.println("Sensor is pressed");
    delay(10); //short delay
  }
  //Save the previous state to see relative changes
  lastState = currentState; 
}
Most of the buttons and switches are of simple construction, so they are subject to a debouncing, as a single press or release of the bottom may trigger many changes in the signal (not just a single swap from LOW to HIGH or opposite). It is because applying force and moving connectors is imperfect and may involve vibration and twinkling. We discuss this problem in the programming patterns chapter.

Light Sensors

Photoresistor

A photoresistor is a sensor that perceives light waves from the environment. The resistance of the photoresistor is changing depending on the intensity of light. The higher the intensity of the light, the lower the sensor's resistance. A light level is determined by applying a constant voltage through the resistor to the sensor forming a voltage divider, and measuring the resulting voltage. Photoresistors are cheap, but the resulting resistance is influenced by temperature and changes slowly, so they are used in applications where speed and accuracy are not crucial. Photoresistors are often used in energy-effective street lighting control.

 Photoresistor symbol
Figure 41: A photoresistor symbol
 Photoresistor
Figure 42: A photoresistor
 Arduino and photoresistor sensor schematics
Figure 43: Arduino and photoresistor sensor schematics

Photoresistor connected, as it is shown in the figure above, gives a lower voltage level while the light is more intense. Results can be read with the following example code. The value will be just a number not expressed in any units, e.g. Lux. To express light intensity in luxes, additional calculations must be encoded in the program.

//Define an analog A0 pin for photoresistor
int photoresistorPin = A0; 
//The analogue reading from the photoresistor 
int photoresistorReading;  
 
void setup()
{
    //Begin serial communication
    Serial.begin(9600);  
    //Initialize the analogue pin of a photoresistor as an input
    pinMode(photoresistorPin, INPUT); 
}
 
void loop()
{
    //Read the value of the photoresistor
    photoresistorReading = analogRead(photoresistorPin); 
    //Print out the value of the photoresistor reading to the serial monitor
    Serial.println(photoresistorReading); 
    delay(10); //Short delay
}

Photodiode

A photodiode is a sensor that converts light energy into electrical current. A current in the sensor is generated by exposing a p-n junction of a semiconductor to the light. Information about the light intensity can be determined by measuring a voltage level. Photodiodes react to changes in light intensity very quickly, so they can be used ad receivers of light-based data transmission systems (e.g. fibre data communication). Solar cells are just large photodiodes.

Photodiodes are used as precise light-level sensors, receivers for remote control, electrical isolators (optocouplers), and proximity detectors.

 Photodiode symbol
Figure 44: A photodiode symbol
 Photodiode
Figure 45: A photodiode
 Arduino and photodiode sensor schematics
Figure 46: Arduino and photodiode sensor schematics

Although the photodiode can generate current, the schematic in the figure above shows its connection similar to the photoresistor in the previous example. In such a circuit, the photodiode changes its current according to a change in light intensity, resulting in the voltage change at the microcontroller's analogue input. As in the example for a photoresistor, the higher the light intensity, the lower the voltage. An example code:

//Define an analog A0 pin for photodiode
int photodiodePin = A0;  
//The analogue reading from the photodiode
int photodiodeReading;  
 
void setup()
{
    //Begin serial communication
    Serial.begin(9600);  
    //Initialize the analogue pin of a photodiode as an input
    pinMode(photodiodePin, INPUT); 
}
 
void loop()
{
    //Read the value of the photodiode
    photodiodeReading = analogRead(photodiodePin); 
    //Print out the value of the photodiode reading to the serial monitor
    Serial.println(photodiodeReading); 
    delay(10); //Short delay
}

Phototransistor

The phototransistor is a normal bipolar transistor with a transparent enclosure that exposes the base-emitter junction to light. In a bipolar transistor, the current that passes through the collector and emitter depends on the base current. In the phototransistor, the collector-emitter current is controlled with light. A phototransistor is slower than a photodiode but can conduct more current; additionally, it amplifies the incoming signal. In specific conditions, if the light is completely off or intense enough to make the output current maximal, a phototransistor can be considered a light-controlled electronic switch (e.g. in optocouplers which are usually connected to digital inputs of the microcontroller).

Phototransistors are used as optical switches, proximity sensors and electrical isolators.

 Phototransistor symbol
Figure 47: A phototransistor symbol
 Phototransistor
Figure 48: An phototransistor
 Arduino and phototransistor schematics
Figure 49: Arduino and phototransistor schematics

An example code:

//Define an analog A1 pin for phototransistor
int phototransistorPin = A1;  
//The analogue reading from the phototransistor
int phototransistorReading;  
 
void setup()
{
    //Begin serial communication
    Serial.begin(9600);  
    //Initialize the analogue pin of a phototransistor as an input
    pinMode(phototransistorPin, INPUT); 
}
 
void loop()
{
    //Read the value of the phototransistor
    phototransistorReading = analogRead(phototransistorPin); 
    //Print out the value of the phototransistor reading to the serial monitor
    Serial.println(phototransistorReading); 
    delay(10); //short delay
}

Optical Sensors

Optocoupler

An optocoupler is a device that combines light-emitting and receiving devices in one package. Mostly it is a combination of the infrared light-emitting diode (LED) and a phototransistor. There are three main types of optocouplers:

  • an optocoupler of a closed pair configuration is enclosed in the dark resin and is used to transfer signals using light. This type of optocoupler is not a sensor itself but is used for ensuring electrical isolation between two circuits;
  • a slotted optocoupler has an open space between the light source and the sensor; light can be obstructed by external objects and thus can influence the sensor signal. It can be used to detect the presence of flat objects, measure rotation speed, vibrations or serve as a bounce-free switch;
  • a reflective pair configuration, the light signal is perceived as a reflection from the object's surface. This configuration is used for proximity detection, surface colour detection and tachometer.


 Optocoupler symbol
Figure 50: An optocoupler symbol
 ELITR9909 reflective optocoupler sensor
Figure 51: ELITR9909 reflective optocoupler sensor
 Arduino Uno and optocoupler schematics
Figure 52: Arduino Uno and optocoupler schematics

An example code:

int optoPin = A0; //Initialize an analogue A0 pin for optocoupler
int optoReading; //The analogue value reading from the optocoupler
 
int objecttreshold = 1000; //Object threshold definition
int whitetreshold = 150; //White colour threshold definition
 
void setup () 
{
  //Begin serial communication
  Serial.begin(9600); 
  //Initialize the analogue pin of the optocoupler as an input
  pinMode(optoPin, INPUT); 
}
 
void loop () 
{
  optoReading = analogRead(optoPin); //Read the value of the optocoupler
  Serial.print ("The reading of the optocoupler sensor is: ");
  Serial.println(optoReading);
 
  //When the reading value is lower than the object threshold
  if (optoReading < objecttreshold) { 
    Serial.println ("There is an object in front of the sensor!");
    //When the reading value is lower than the white threshold
    if (optoReading < white threshold) { 
      Serial.println ("Object is in white colour!");
    } else { //When the reading value is higher than the white threshold
      Serial.println ("Object is in dark colour!");
    }
  }
  else { //When the reading value is higher than the object threshold
    Serial.println ("There is no object in front of the sensor!");
  }
  delay(500); //Short delay
}

Colour Sensor

This type of sensor gives information about the colour of the light illuminating the sensor surface. Because computers often use RGB (red, green, blue) colour scheme sensor returns three values representing the intensity of three components. Colour sensors usually contain white LEDs to illuminate the surface, which colour should be distinguished by them. The colour sensor uses SPI or TWI interface to send readings. Some models of colour sensors include an additional gesture detector which recognises simple gestures (up, down, left, right).

 TCS34725 RGB colour sensor module
Figure 53: TCS34725 RGB colour sensor module
#include <Wire.h>
#include "Adafruit_TCS34725.h"
 
// Example code for the TCS34725 library by Adafruit
 
// Sensor class 
Adafruit_TCS34725 rgb_sensor = Adafruit_TCS34725();
 
void setup(void) {
  Serial.begin(9600);
 
  if (rgb_sensor.begin()) {  //Initialise RGB sensor
    Serial.println("RGB sensor present");
  } else {
    Serial.println("No TCS34725 found");
    while (1);
  }
}
 
void loop(void) {
  uint16_t r, g, b, unfiltered, lux;
 
  rgb_sensor.getRawData(&r, &g, &b, &unfiltered);   //read RGB and unfiltered light intensity
  lux = rgb_sensor.calculateLux(r, g, b);  //calculate illuminance in Lux
 
  Serial.print("Lux: ");     //print calculated Lux value
  Serial.print(lux, DEC);
  Serial.print(" - ");
 
  Serial.print("R: ");       //print red component value
  Serial.print(r, DEC); 
  Serial.print(" ");
 
  Serial.print("G: ");       //print green component value 
  Serial.print(g, DEC); 
  Serial.print(" ");
 
  Serial.print("B: ");       //print blue component value
  Serial.print(b, DEC); 
  Serial.print(" ");
 
  Serial.print("C: ");       //print unfiltered sensor value
  Serial.print(unfiltered, DEC); 
  Serial.println(" ");
 
  delay(1000);
}

Electrical Characteristic Sensors

Electrical characteristic sensors are used to measure the voltage and amperage of the electric current. When the voltage and current sensors are used concurrently, the consumed power of the device can be determined. Electrical characteristic sensors can determine whether the device's circuit is working properly. Different sensor circuits must be used to measure direct current (DC) and alternating current (AC). If the parameters of the mains are to be measured, it must be done using transformers for safety reasons.

Voltage Sensor

A voltage sensor is a device or circuit for voltage measurement. A simple DC (direct current) voltage sensor consists of a voltage divider circuit with an optional amplifier for a very small voltage measure. For measuring the AC (alternating current), the input is connected to the rectifier diode or bridge to rectify AC to DC and a capacitor to flatten the voltage. The resulting voltage can be measured with an analogue, digital converter of the microcontroller. For safety, while measuring the mains voltage, an optoelectrical isolator should be added at the output, or a transformer to additionally lower the voltage at the input.

A voltage sensor can detect a power failure and measure if the voltage is in the range required. IoT applications include monitoring appliances, power lines, and power supplies.

 Voltage sensor module
Figure 54: Voltage sensor module 0–25 V
 Arduino and voltage sensor schematics
Figure 55: Arduino and voltage sensor schematics

The example code:

//Define an analogue A1 pin for voltage sensor
int voltagePin = A1; 
//The result of the analogue reading from the voltage sensor
int voltageReading;  
 
float vout = 0.0;
float vin = 0.0;
float R1 = 30000.0; //  30 kΩ resistor 
float R2 = 7500.0; //  7.5 kΩ resistor
 
void setup()
{
    //Begin serial communication
    Serial.begin(9600);  
    //Initialize the analogue pin as an input
    pinMode(voltagePin, INPUT); 
}
 
void loop()
{
    //Read the value of the voltage sensor
    voltageReading = analogRead(voltagePin); 
    vout = (voltageReading * 5.0) / 1024.0;
    vin = vout / (R2/(R1+R2));
 
    Serial.print("Voltage is: ");
    //Print out the value of the voltage to the serial monitor
    Serial.println(vin); 
    delay(10); //Short delay
}

Current Sensor

A current sensor is a device or a circuit for current measurement. A simple DC sensor consists of a high-power resistor with low resistance. The current value is obtained by measuring the voltage on the resistor and applying a formula derived from Ohm's law. Other non-invasive measurement methods involve hall effect sensors for DC and AC and inductive coils (current transformer) for AC. Current sensors are used to determine the power consumption and to detect whether the device is turned on or shorted.

Make a photo of the current transformer.

 Current transformer module for AC
Figure 56: Current transformer module for AC
 Analogue current meter module
Figure 57: Analogue current meter module 0–50 A
 Arduino and current sensor module schematics
Figure 58: Arduino and current sensor module schematics

The example code:

//Define an analogue A0 pin for current sensor
const int currentPin = A0; 
//Scale factor of the sensor use 100 for 20 A Module and 66 for 30 A Module
int mVperAmp = 185; 
int currentReading;
int ACSoffset = 2500; 
double Voltage;
double Current;
 
void setup(){ 
 Serial.begin(9600);
}
 
void loop(){
 
 currentReading = analogRead(currentPin);
 Voltage = (currentReading / 1024.0) * 5000; //Gets you mV
 Current = ((Voltage - ACSoffset) / mVperAmp); //Calculating current value
 
 Serial.print("Raw Value = " ); //Shows pre-scaled value 
 Serial.print(currentReading); 
 Serial.print("\t Current = "); //Shows the voltage measured
 //The '3' after current allows to display 3 digits after the decimal point
 Serial.println(Current,3); 
 delay(1000); //Short delay

Proximity and Distance Sensors

Infrared Sensor

An infrared (IR) proximity sensor is used to detect objects and to measure the distance to them without any physical contact. IR sensor consists of an infrared emitter, a receiving sensor or array of sensors and a signal processing logic. The output of a sensor differs depending on the type – simple proximity detection sensor outputs HIGH or LOW level when an object is in its sensing range, but sensors which can measure distance output an analogue signal or use some communication protocol, like I2C to send sensor measuring results. IR sensors are used in robotics to detect obstacles located a few millimetres to several meters from the sensor and in mobile phones to help detect accidental screen touching.

 Distance Sensor GP2Y0A21YK0F
Figure 59: Distance Sensor GP2Y0A21YK0F
 Arduino and IR proximity sensor circuit
Figure 60: Arduino and IR proximity sensor circuit

An example code:

int irPin = A0;  //Define an analogue A0 pin for IR sensor
int irReading;  //The result of an analogue reading from the IR sensor
 
void setup()
{
    //Begin serial communication
    Serial.begin(9600);  
    //Initialize the analogue pin of an IR sensor as an input
    pinMode(irPin, INPUT); 
}
 
void loop()
{
    //Read the value of the IR sensor
    irReading = analogRead(irPin); 
    //Print out the value of the IR sensor reading to the serial monitor
    Serial.println(irReading); 
    delay(10); //Short delay
}

Ultrasonic Sensor

The ultrasonic sensor measures the distance to objects by emitting short ultrasound sound pulse and measuring its returning time. The sensor consists of an ultrasonic emitter and receiver; sometimes, they are combined into a single device for emitting and receiving. Ultrasonic sensors can measure greater distances and cost less than infrared sensors, but are more imprecise and interfere which each other measurement if more than one is used. Simple sensors have a trigger pin and an echo pin; when the trigger pin is set high for a small amount of time, ultrasound is emitted, and on the echo pin, response time is measured. Ultrasonic sensors are used in car parking sensors and robots for proximity detection.

 Ultrasonic proximity sensor HC-SR04}
<caption>Ultrasonic proximity sensor HC-SR04</caption>
</figure>

Examples of IoT applications are robotic obstacle detection and room layout scanning.

<figure label>
{{ :en:iot-open:getting_familiar_with_your_hardware_rtu_itmo_sut:arduino_and_arduino_101_intel_curie:sch_apz_shemas_ultrasound_proximity.png?400 | Arduino and ultrasound proximity sensor circuit
Figure 61: Arduino and ultrasound proximity sensor circuit

An example code:

int trigPin = 2;  //Define a trigger pin D2
int echoPin = 4;  //Define an echo pin D4
 
void setup()
{
    Serial.begin(9600); //Begin serial communication
    pinMode(trigPin, OUTPUT); //Set the trigPin as an Output
    pinMode(echoPin, INPUT); //Set the echoPin as an Input
}
 
void loop()
{
    digitalWrite(trigPin, LOW);  //Clear the trigPin
    delayMicroseconds(2);
 
    //Set the trigPin on HIGH state for 10 μs
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
 
    //Read the echoPin, return the sound wave travel time in microseconds
    duration = pulseIn(echoPin, HIGH); 
    //Calculating the distance 
    distance = duration*0.034/2; 
 
    //Printing the distance on the Serial Monitor
    Serial.print("Distance: ");  
    Serial.println(distance);
}

Motion Detector

The motion detector is a sensor that detects moving objects, mostly people. Motion detectors use different technologies, like passive infrared sensors, microwaves and the Doppler effect, video cameras, and ultrasonic and IR sensors. Passive IR (PIR) sensors are the simplest motion detectors that sense people by detecting changes in IR radiation that is emitted through the skin. When the motion is detected, the output of a motion sensor is a digital HIGH/LOW signal.

Motion sensors are used in security alarm systems, automated lights and door control. As an example in IoT, the PIR motion sensor can be used to detect motion in security systems in a house or any building.

 PIR motion sensor
Figure 62: PIR motion sensor
 Arduino and PIR motion sensor circuit
Figure 63: Arduino and PIR motion sensor circuit

An example code:

//Passive Infrared (PIR) sensor output is connected to the digital 2 pin
int pirPin = 2; 
//The digital reading from the PIR output
int pirReading;      
 
void setup(void) {
  //Begin serial communication
  Serial.begin(9600);   
  //Initialize the PIR digital pin as an input
  pinMode(pirPin, INPUT); 
}
 
void loop(void) {
  //Read the digital value of the PIR motion sensor
  pirReading = digitalRead(pirPin); 
  //Print out
  Serial.print("Digital reading = "); 
  Serial.println(pirReading);
 
  if(pirReading == HIGH) {  //Motion was detected
    Serial.println("Motion Detected");
  }
 
  delay(10);
}

Fluid Level Sensor

A level sensor detects the level of fluid or fluidised solid. Level sensors can be divided into two groups:

  • continuous level sensors that can detect the exact position of the fluid. For level detection, usually, the proximity sensors, like ultrasonic or infrared, are used. Capacitive sensors can also be used by recording the changing capacitance value depending on the fluid level. The output can be either analogue or digital value;
  • point-level sensors can detect whether a fluid is above or below the sensor. A membrane with air pressure or changes in conductivity or capacitance can be used for level detection, also a floating or mechanical switch. The output is usually a digital value that indicates HIGH or LOW value.

Fluid level sensors can be used for smart waste management, for measuring tank levels, diesel fuel gauging, liquid assets inventory, chemical manufacturing, high or low-level alarms, and irrigation control.

 Liquid level sensor
Figure 64: Liquid level sensor
 Arduino Uno and liquid level sensor schematics
Figure 65: Arduino Uno and liquid level sensor schematics

An example code:

int levelPin = 6; //Liquid level sensor output is connected to the digital 6 pin
int levelReading; //Stores level sensor detection reading
 
void setup(void) {
  Serial.begin(9600);   //Begin serial communication
  pinMode(levelPin, INPUT); //Initialize the level sensor pin as an input
}
 
void loop(void) {
  levelReading = digitalRead(levelPin); //Read the digital value of the level sensor
  Serial.print("Level sensor value: "); //Print out
  Serial.println(levelReading);
  delay(10); //Short delay
}

Angle & Orientation Sensors

Potentiometer

A potentiometer is a type of resistor, the resistance of which can be adjusted using a mechanical lever. The device consists of three terminals. The resistor between the first and the third terminal has a fixed value, but the second terminal is connected to the lever. Whenever the lever is turned, a slider of the resistor is moved; it changes the resistance between the second terminal and side terminals. Variable resistance causes the change of the voltage, which can be measured to determine the position of the lever. Thus, the potentiometer output is an analogue value.

Potentiometers are commonly used as a control level, for example, a volume level for the sound and joystick position. They can also be used to determine the angle in feedback loops with motors, such as servo motors.

 Symbol of a potentiometer
Figure 66: A symbol of a potentiometer
 Potentiometer
Figure 67: A potentiometer
 Arduino and potentiometer circuit
Figure 68: Arduino and potentiometer circuit

An example code:

//Potentiometer sensor output is connected to the analogue A0 pin
int potentioPin = A0; 
//The analogue reading from the potentiometer output
int potentioReading;      
 
void setup(void) {
  //Begin serial communication
  Serial.begin(9600);   
  //Initialize the potentiometer analogue pin as an input
  pinMode(potentioPin, INPUT); 
}
 
void loop(void) {
  //Read the analogue value of the potentiometer sensor
  potentioReading = analogRead(potentioPin); 
  Serial.print("Potentiometer reading = "); //Print out
  Serial.println(potentioReading);
  delay(10);
}

The Inertial Measurement Unit (IMU)

An IMU is an electronic device consisting of an accelerometer, gyroscope and sometimes a magnetometer. The combination of these sensors returns the object's orientation in 3D space. IMU sensors can present the object's current position and movement, expressed with at most six values called the DOF (Degrees Of Freedom). Three values express the linear movements that can be measured by the accelerometer:

  • moving forward/backwards,
  • moving left/right,
  • moving up/down.

Another three values present the rotation in three axes that can be measured by gyroscope:

  • roll side to side,
  • pitch forward and backwards,
  • yaw left and right.

A gyroscope is a sensor that measures the angular velocity. The sensor is made with microelectromechanical system (MEMS) technology and is integrated into the chip. The sensor output can be analogue or digital, using I2C or SPI interface. Gyroscope microchips can vary in the number of axes they can measure. The available number of the axis is 1, 2 or 3 axes in the gyroscope. A gyroscope is commonly used with an accelerometer to precisely determine the device's orientation, position and velocity. Gyroscope sensors are used in aviation, navigation and motion control.

An accelerometer measures the acceleration of the object. The sensor uses microelectromechanical system (MEMS) technology, where capacitive plates are attached to springs. When acceleration force is applied to the plates, the capacitance is changed; thus, it can be measured. Accelerometers can have 1 to 3 axis. The 3-axis accelerometer can detect the device's orientation, shake, tap, double tap, fall, tilt, motion, positioning, shock or vibration. Outputs of the sensor are usually digital interfaces like I2C or SPI. The accelerometer is often used with a gyroscope for precise measurement of the object's movement and orientation in space. Accelerometers are used to measure objects' vibrations, including cars, industrial devices, and buildings, and to detect volcanic activity. In IoT applications, it can also be used for accurate motion detection for medical and home appliances, portable navigation devices, augmented reality, smartphones and tablets.

A magnetometer is a sensor that can measure the device's orientation to the Earth's magnetic field. A magnetometer is used as a compass in outdoor navigation for mobile devices, robots, and quadcopters.

There are different elements available that allow measuring linear accelerations, angular accelerations, and magnetic fields, all in three axes. There exist elements that combine two (called 6-axis or 6-DOF) or all (9-axis, 9-DOF) measurement units. Popular integrated circuits are MPU6050 (3-axes gyro + 3-axes accelerometer), MPU9250, and BNO055 (3-axes gyro, 3-axes accelerometer, 3-axes magnetometer). All of them can be programmed in an Arduino environment with the use of dedicated libraries. The latter automatically calculates additional information like gravity vector, and absolute orientation expressed as an Euler vector or as a quaternion.

Take a photo of MPU6050.

 IMU MPU6050 module
Figure 69: IMU MPU6050 module

Take a photo of MPU9250.

 IMU MPU9250 module
Figure 70: IMU MPU9250 module
 IMU BNO055 module
Figure 71: IMU BNO055 module
 Arduino Uno and IMU BNO055 module schematics
Figure 72: Arduino Uno and IMU BNO055 module schematics

The example code:

//Library for I2C communication
#include <Wire.h>
//Downloaded from https://github.com/adafruit/Adafruit_Sensor
#include <Adafruit_Sensor.h>
//Downloaded from https://github.com/adafruit/Adafruit_BNO055 
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>
Adafruit_BNO055 bno = Adafruit_BNO055(55);
void setup(void)
{
bno.setExtCrystalUse(true);
}
void loop(void)
{
//Read sensor data
sensors_event_t event;
bno.getEvent(&event);
//Print X, Y And Z orientation
Serial.print("X: ");
Serial.print(event.orientation.x, 4);
Serial.print("\tY: ");
Serial.print(event.orientation.y, 4);
Serial.print("\tZ: ");
Serial.print(event.orientation.z, 4);
Serial.println("");
delay(100);
}

Environment Sensors

Temperature Sensor

A temperature sensor is a device that is used to determine the temperature of the surrounding environment. Most temperature sensors work on the principle that the material's resistance changes depending on its temperature. The most common temperature sensors are:

  • thermocouple – consists of two junctions of dissimilar metals,
  • thermistor – includes the temperature-dependent resistor,
  • resistive temperature detector – is made of a pure metal coil.

The main difference between sensors is the measured temperature range, precision and response time. Temperature sensor usually outputs the analogue value, but some existing sensors have a digital interface [50]. The thermistor can have a positive (PTC) or negative (NTC) thermal coefficient. For PTC, resistance rises with rising temperature, while resistance decreases in higher temperatures for NTC. An analogue thermistor must calculate the value read if the result should be presented in known units. Digital temperature sensors usually express the result in Celsius degrees or other units.

Temperature sensors are most commonly used in environmental monitoring devices and thermoelectric switches. In IoT applications, the sensor can be used for greenhouse temperature monitoring, warehouse temperature monitoring to avoid freezing, fire suppression systems and tracking the temperature of the soil, water and plants.

 Thermistor
Figure 73: A thermistor
 Arduino and thermistor circuit
Figure 74: Arduino and thermistor circuit

An example code:

//Thermistor sensor output is connected to the analogue A0 pin
int thermoPin = 0; 
//The analogue reading from the thermistor output
int thermoReading;      
 
void setup(void) {
  //Begin serial communication
  Serial.begin(9600);   
  //Initialize the thermistor analogue pin as an input
  pinMode(thermoPin, INPUT); 
}
 
void loop(void) {
  //Read the analogue value of the thermistor sensor
  thermoReading = analogRead(thermoPin); 
  Serial.print("Thermistor reading = "); //Print out
  Serial.println(thermoReading);
  delay(10);
}

Digital Temperature Sensor

Digital temperature sensors automatically convert the temperature reading into some known unit, e.g. Celsius, Fahrenheit or Kelvin Degrees. Digital thermometers use one of the popular communication links. An example of a digital thermometer is DS18B20 by Dallas Semiconductors. It uses a 1-Wire communication protocol.

Take DS18B20 photo DS18B20 photo Draw DS18B20 connection DS18B20 connection

#include <OneWire.h>           // library for 1-Wire protocol
#include <DallasTemperature.h> //library for Dallas DS18B20 digital thermometer
 
const int SENSOR_PIN = 13;     //DS18B20 pin
 
OneWire oneWire(SENSOR_PIN);   //oneWire class
DallasTemperature tempSensor(&oneWire); // connect oneWire to DallasTemperature library
 
float tempCelsius;       // temperature in Celsius degrees
 
void setup()
{
  Serial.begin(9600);    //initialize serial port
  tempSensor.begin();    //initialize DS18B20
}
 
void loop()
{
  tempSensor.requestTemperatures();             //command to read temperatures
  tempCelsius = tempSensor.getTempCByIndex(0);  //read temperature (in Celsius)
 
  Serial.print("Temp: ");
  Serial.print(tempCelsius);    // print the temperature
  Serial.println(" C");
 
  delay(1000);
}

Humidity Sensor

A humidity sensor (hygrometer) is a sensor that detects the amount of water or water vapour in the environment. The most common principle of air humidity sensors is the change of capacitance or resistance of materials that absorb moisture from the environment. Soil humidity sensors measure the resistance between the two electrodes. The resistance between electrodes is influenced by soluble salts and water amounts in the soil. The output of a humidity sensor is an analogue signal value or digital value sent with some popular protocols [51].

Examples of IoT applications are monitoring of humidors, greenhouse humidity, agriculture, art gallery and museum environment.

 Temperature and humidity sensor module
Figure 75: Temperature and humidity sensor module
 Arduino Uno and humidity sensor schematics
Figure 76: Arduino Uno and humidity sensor schematics

An example code [52]:

#include <dht.h>
 
dht DHT;
 
#define DHT_PIN 7
 
void setup(){
  Serial.begin(9600);
}
 
void loop()
{
  int chk = DHT.read11(DHT_PIN);
  Serial.print("Humidity = ");
  Serial.println(DHT.humidity);
  delay(1000);
}

Sound Sensor

A sound sensor is a sensor that detects vibrations in a gas, liquid or solid environment. At first, the sound wave pressure makes mechanical vibrations, which transfer to changes in capacitance, electromagnetic induction, light modulation or piezoelectric generation to create an electric signal. The electrical signal is then amplified to the required output levels. Sound sensors can be used to record sound and detect noise and its level.

Sound sensors are used in drone detection, gunshot alert, seismic detection and vault safety alarms.

 Digital sound detector sensor module
Figure 77: Digital sound detector sensor module
 Arduino Uno and sound sensor schematics
Figure 78: Arduino Uno and sound sensor schematics

An example code:

//Sound sensor output is connected to the digital 7 pin
int soundPin = 7; 
//Stores sound sensor detection readings
int soundReading = HIGH; 
 
void setup(void) {
  //Begin serial communication
  Serial.begin(9600);   
  //Initialize the sound detector module pin as an input
  pinMode(soundPin, INPUT); 
}
 
void loop(void) {
  //Read the digital value whether the sound has been detected
  soundReading = digitalRead(soundPin); 
  if (soundPin==LOW) { //When sound detector detected the sound
    Serial.println("Sound detected!"); //Print out
  } else { //When the sound is not detected
    Serial.println("Sound not detected!"); //Print out
  }
  delay(10);
}

Chemical and Gas Sensor

Gas sensors are a sensor group that can detect and measure the concentration of certain gasses in the air. The working principle of electrochemical sensors is to absorb the gas and create current from an electrochemical reaction. For process acceleration, a heating element can be used. For each type of gas, different kind of sensor needs to be used. Multiple types of gas sensors can also be combined in a single device. The single gas sensor output is an analogue signal, but devices with multiple sensors used have a digital interface. The smoke or air pollution sensors usually use LED or laser that emits light and a detector which is normally shaded from the light. If there are particles of smoke or polluted air inside the sensor, the light is reflected by them, which can be observed by the detector.

Gas sensors are used for safety devices, air quality control, and manufacturing equipment. IoT applications include air quality control management in smart buildings and smart cities or toxic gas detection in sewers and underground mines.

 MQ-7 gas sensor
Figure 79: MQ-7 gas sensor
 Arduino Uno and MQ2 gas sensor schematics
Figure 80: Arduino Uno and MQ2 gas sensor schematics

An example code:

int gasPin = A0; //Gas sensor output is connected to the analog A0 pin
int gasReading; //Stores gas sensor detection reading
 
void setup(void) {
  Serial.begin(9600);   //Begin serial communication
  pinMode(gasPin, INPUT); //Initialize the gas detector pin as an input
}
 
void loop(void) {
  gasReading = analogRead(gasPin); //Read the analog value of the gas sensor
  Serial.print("Gas detector value: "); //Print out
  Serial.println(gasReading);
  delay(10); //Short delay
}

Smoke and Air Pollution Sensors

The smoke sensors usually emit LED light, and a detector is normally shaded from the light. If there are particles of smoke present inside the sensor, the light is reflected by them, which can be observed by the detector. Smoke detectors are used in fire alarm systems. The air pollution sensors usually use a laser directed onto the detector. Between the laser and detector, the thin stream of air flows and pollution particles create shades on the detector. Thus the detector can distinguish the sizes of particles and count the number of them. Air pollution sensors are used in air purifiers and in air quality measurement stations to monitor current air conditions, mainly in cities. Because the air pollution sensor generates more data, the serial connection is often used for reading measurement results. An example of an air pollution sensor that can count particles of PM1.0, PM2.5, and PM10 is PMS5003.

Draw PMS5003 connection PMS5003 photo Draw PMS5003 connection PMS5003 connection

An example code that uses the PMS5003 sensor:

#define length 31   //Size of the data frame from the sensor 
                    //(does not include the start indicator - 0x42)
unsigned char buffer[length];
 
int PM_1_0=0;    //variale to hold PM1.0 value of the air sensor
int PM_2_5=0;    //variale to hold PM2.5 value of the air sensor
int PM_10=0;     //variale to hold PM10 value of the air sensor
 
void setup()
{
//Initialise the serial port for 9600 b/s (should be compatible with the sensor type)
  Serial.begin(9600);   
}
 
void loop()
{
  if(Serial.find(0x42)) //detecting 0x42 means start of data frame from sensor
  {    
    Serial.readBytes(buffer,length);
    if(buffer[0] == 0x4d)
    {  
      PM_1_0=((buffer[3]<<8) + buffer[4]); //count PM1.0 value of the air detector module
      PM_2_5=((buffer[5]<<8) + buffer[6]);//count PM2.5 value of the air detector module
      PM_10 =((buffer[7]<<8) + buffer[8]); //count PM10 value of the air detector module 
 
      Serial.print("PM1.0: ");  
      Serial.print(PM_1_0);
      Serial.println(" ug/m3");            
 
      Serial.print("PM2.5: ");  
      Serial.print(PM_2_5);
      Serial.println(" ug/m3");     
 
      Serial.print("PM10:  ");  
      Serial.print(PM_10);
      Serial.println(" ug/m3");   
    } 
  }
}

Air Pressure Sensor

Air pressure sensors can measure the absolute pressure in the surrounding environment. Some popular sensors use a piezo-resistive sensing element which is then connected to the amplifier and analogue, digital converter. Frint-end uses the logic to interface the microcontroller. Usually, barometric sensor readings depend on the temperature, so they include the temperature sensor for temperature compensation of the pressure. Popular examples of barometric sensors are BME280 and BMP280. Both include barometric sensors and temperature sensors built in for compensation and possible measurement, while BME280 also includes a humidity sensor. Communication with these sensors is done with I2C or SPI bus.

Barometric sensors are used in weather stations, home automation for heating, venting, air conditioning (HVAC), and airflow measurement. Because air pressure varies with altitude, they are often used in altimeters.

Take BME280 photo BME280 photo Draw BME280 connection BME280 connection

An example code of BME280 use is below [53]:

#include <Arduino.h>
#include <Bme280.h>
 
Bme280TwoWire sensor;
 
void setup() {
  Serial.begin(9600);
  Wire.begin(D2, D1);
 
  Serial.println();
 
  sensor.begin(Bme280TwoWireAddress::Primary);
  sensor.setSettings(Bme280Settings::indoor());
}
 
void loop() {
  auto temperature = String(sensor.getTemperature()) + " °C";
  auto pressure = String(sensor.getPressure() / 100.0) + " hPa";
  auto humidity = String(sensor.getHumidity()) + " %";
 
  String measurements = temperature + ", " + pressure + ", " + humidity;
  Serial.println(measurements);
 
  delay(10000);
}

Other Sensors

Hall sensor

A Hall effect sensor detects strong magnetic fields, their polarities and the relative strength of the field. In the Hall effect sensors, a magnetic force influences current flow through the semiconductor material and creates a measurable voltage on the sides of the semiconductor. Sensors with analogue output can measure the strength of the magnetic field, while digital sensors give HIGH or LOW output value, depending on the presence of the magnetic field.

Hall effect sensors are used in magnetic encoders for speed and rotation measurements. They can replace mechanical switches in keyboards and proximity switches because they do not require contact, which ensures high reliability. An example application can be sensing the position of rotary valves.

 Hall-effect sensor module
Figure 81: Hall-effect sensor module
 Arduino Uno and Hall sensor schematics
Figure 82: Arduino Uno and Hall sensor schematics

The example code:

int hallPin = A0; //Hall sensor output is connected to the analogue A0 pin
int hallReading;  //Stores Hall sensor reading
 
void setup(void) {
  Serial.begin(9600);      //Begin serial communication
  pinMode(hallPin, INPUT); //Initialize the Hall sensor pin as an input
}
 
void loop(void) {
  hallReading = analogRead(hallPin);   //Read the analogue value of the Hall sensor
  Serial.print("Hall sensor value: "); //Print out
  Serial.println(hallReading);
  delay(10); //Short delay
}

Global Positioning System

A GPS receiver is a device that can receive information from a global navigation satellite system and calculate its position on the Earth. A GPS receiver uses a constellation of satellites and ground stations to compute position and time almost anywhere on Earth. GPS receivers are used for navigation only in the outdoor area because they need to receive signals from the satellites, which is difficult inside the buildings. The GPS location's precision can vary depending on the number of visible satellites, weather conditions, and current satellites' placement. Often the GPS receiver is connected to a microcontroller with a serial communication port and sends information according to the NMEA scheme.

A GPS receiver is used for device location tracking. Real applications might be, e.g., pet, kid or personal belonging location tracking.

 Grove GPS receiver module
Figure 83: Grove GPS receiver module
 Arduino Uno and Grove GPS receiver schematics
Figure 84: Arduino Uno and Grove GPS receiver schematics

The example code [54]:

#include <SoftwareSerial.h>
SoftwareSerial SoftSerial(2, 3);
unsigned char buffer[64];    //Buffer array for data receive over serial port
int count=0;                 //Counter for buffer array
void setup()
{
    SoftSerial.begin(9600);  //The SoftSerial baud rate
    Serial.begin(9600);      //The Serial port of Arduino baud rate.
}
 
void loop()
{
    if (SoftSerial.available())  //If data is coming from software serial port 
                                 // ==> Data is coming from SoftSerial shield
    {
        while(SoftSerial.available())  //Reading data into char array
        {
            buffer[count++]=SoftSerial.read(); //Writing data into array
            if(count == 64)break;
        }
        Serial.write(buffer,count);    //If no data transmission ends, 
                                       //Write buffer to hardware serial port
        clearBufferArray();            //Call clearBufferArray function to clear 
                                       //The stored data from the array
        count = 0;                     //Set the counter of the while loop to zero 
    }
    if (Serial.available())       //If data is available on hardware serial port 
                                       // ==> Data is coming from a PC or notebook
    SoftSerial.write(Serial.read());   //Write it to the SoftSerial shield
}
 
 
void clearBufferArray()                //Function to clear buffer array
{
    for (int i=0; i<count;i++)
    {
        buffer[i]=NULL;
    }                         //Clear all content of an array with NULL
}

Actuators and output devices

An output device is a unit that changes an electrical signal coming from the microcontroller into the physical parameter. It can generate or modify light, sound, force, pressure and other physical values that influence other devices nearby or the surrounding environment. Some output elements can be connected directly to the microcontroller's pins, and some require higher voltage or current, so they need an additional electronic circuit called the driver. Output devices can be divided into groups based on the physical phenomenon they control. Popular output devices include LEDs, displays, motors (actuators), speakers, and buzzers.

Optical Output Devices

Light-Emitting Diode

Unlike the other diodes, the light-emitting diode, also called LED, is a special type that emits light. LED has a completely different body which is made of transparent plastic that protects the diode and lets it emit light. Like the other diodes, LED conducts the current in only one way, so connecting it to the scheme is essential. There are two safe ways how to determine the direction of the diode:

  • in the cathodes side of the diode its side is chipped,
  • anodes leg usually is longer than the cathodes leg.
 5 mm Red LED
Figure 85: 5 mm Red LED

The LED is one of the most efficient light sources. Unlike incandescent light bulbs, LED transforms most of the power into light, not warmth; it is more durable, works for a more extended period and can be manufactured in a smaller size.

The LED colour is determined by the semiconductors material. Diodes are usually made from silicon, then LEDs are made from elements like gallium phosphate, silicon carbide and others. Because the semiconductors used are different, the voltage needed for the LED to shine is also different.

When the LED is connected to the voltage and turned on, a huge current starts to flow through it, and it can damage the diode. That is why all LEDs have to be connected in series with a current-limiting resistor.

Current limiting resistors resistance is determined by three parameters:

  • I_D – Current that can flow through the LED,
  • U_D – Voltage that is needed to turn on the LED,
  • U – Combined voltage for LED and resistor.

To calculate the resistance needed for a diode, this is what you have to do.

  1. Find out the voltage needed for the diode to work U_D; you can find it in the diode parameters table.
  2. Find out the amperage needed for the LED to shine I_D; it can be found in the LEDs datasheet, but if you can’t find it, then 20 mA current is usually a correct and safe choice.
  3. Find out the combined voltage for the LED and resistor; usually, it is the feeding voltage for the scheme.
  4. Insert all the values into this equation: R = (U – U_D) / I_D.
  5. You get the resistance for the resistor for the safe use of the LED.
  6. Find a resistor with a nominal value that is the same or slightly bigger than the calculated resistance.
 Arduino Uno and LED control schematic
Figure 86: Arduino Uno and LED control schematic

An example of the blinking LED code:

int ledPin = 8;//Defining the pin of the LED
 
void setup()
{   
    pinMode(ledPin,OUTPUT); //The LED pin is set to output
}
 
void loop() 
{   
    //Set pin output signal to HIGH – LED is working
    digitalWrite(ledPin,HIGH); 
    //Belay of 1000 ms
    delay(1000); 
 
    //Set pin output signal to LOW – LED is not working
    digitalWrite(ledPin,LOW); 
    //Delay of 1000 ms
    delay(1000);
}

LED's brightness can be controlled with a PWM signal as described in the “Manipulating analogue signals” chapter. There exist LEDs with more than one light-emitting chip in one enclosure. They are made as two-coloured or RGB elements with coloured controlled separately. There are two internal configurations of such elements:

  • common anode - anodes of all internal LEDs are connected together
  • common cathode - cathodes of all internal LEDs are connected
 Connection of RGB common anode LED to Arduino
Figure 87: Connection of RGB common anode LED to Arduino
 Connection of RGB common cathode LED to Arduino
Figure 88: Connection of RGB common cathode LED to Arduino

[✓ ktokarz, 2023-08-22]picture with common anode and common cathode LEDs

Digital LED

Digital RGB LED Digital LED does not have anode or cathode connections available externally. They have power supply pins and two pins for data transmission, one for input, second for output. The input accepts the digital signal from the microcontroller to set the brightness of all three internal LEDs. Output is used to connect the input of another LED to form a series of LEDs. Digital LEDS are available as single elements but also as strips, rings or matrices that can be controlled by a microcontroller with one pin only. What is important every LED can shine in different colours allowing it to create interesting visual effects. An example of a popular digital LED is WS2812. A special protocol is used to transmit data. One LED requires 24 bits (1 byte for red, 1 for green, and 1 for blue) to set the colour. After receiving its data LED resends any further byte to the following LEDs in the chain. There are software libraries for Arduino and other platforms available.

Displays

Using a display is a quick way to get feedback information from the device. There are many display technologies. For IoT solutions, low-power, easy-to-use displays are used:

  • 7-segment LED display
  • LED matrix display
  • liquid-crystal display (LCD),
  • organic light-emitting diode display (OLED),
  • electronic ink display (E-ink).

7-segment LED display [✓ ktokarz, 2023-08-22] 7-segment LED display The seven-segment LED display is built with seven LEDs forming the shape that makes it possible to display symbols similar to digits, and even some letters. Usually, the eighth LED is added as the decimal point. 7-segment displays can have similar internal connections as RGB LEDs, common anode or common cathode. If there is more than one digit in the element all the same segments are also connected together. Such displays need special controllers or the part of the software that displays separate digits in a sequence one by one. To avoid unnecessary blinking or difference in the brightness of digits, software for sequential displays is written with the use of timers and interrupts. As for the RGB LEDs, 7-segment displays need a separate resistor for every segment.

Figure 89: 7 segment LED display

LED matrix display [✓ ktokarz, 2023-08-22] LED matrix display LED matrix displays offer the possibility of displaying not only digits and letters but also some pictograms and symbols. The most popular versions have 8 rows and 8 columns, or 7 rows and 5 columns, but it is possible to find other configurations. As for the 7-segment displays, there are common anode and common cathode configurations. For a common anode, all anodes in one row and all cathodes in one column are connected together. For a common cathode, all cathodes in one row and all anodes in one column are connected together. Modern LED matrix displays have built-in controllers or are made with the use of digital RGB LEDs which makes them possible to display pictures and even videos.

photo of P6 matrix display

Liquid-Crystal Display (LCD)

Monochrome LCD uses modulating properties of liquid crystal to block the passing-through light. Thus when a voltage is applied to a pixel, it is dark. A display consists of layers of electrodes, polarising filters, liquid crystals and a reflector or back-light. Liquid crystals do not emit light directly but through reflection or backlight. Because of this reason, they are more energy efficient. Small, monochrome LCDs are widely used in devices to show a little numerical or textual information like temperature, time, device status etc. The most popular LCD device is an alphanumerical 2×16 characters display based on the HD44780 controller. There also exist graphic monochrome and colour TFT displays that use LCD technology. LCD modules commonly come with an onboard control circuit and are controlled through parallel or serial interfaces.

 Blue 16 × 2 LCD display
Figure 90: Blue 16 × 2 LCD display
 Arduino and LCD screen schematic
Figure 91: Arduino and LCD screen schematics

The example code:

#include <LiquidCrystal.h> //include LCD library
 
//Define LCD pins
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
//Create an LCD object with predefined pins 
LiquidCrystal lcd(rs, en, d4, d5, d6, d7); 
 
void setup() {
  lcd.begin(16, 2); //Set up the LCD's number of columns and rows
  lcd.print("hello, world!"); //Print a message to the LCD
}
 
void loop() {
  //Set the cursor to column 0, line 1 – line 1 is the second row 
  //Since counting begins with 0
  lcd.setCursor(0, 1); 
  //Print the number of seconds since the reset
  lcd.print(millis() / 1000); 
}

Organic Light-Emitting Diode Display (OLED)

OLED display uses electroluminescent materials that emit light when the current passes through these materials. The display consists of two electrodes and a layer of an organic compound. OLED displays are thinner than LCDs, have higher contrast, and can be more energy efficient depending on usage. OLED displays are commonly used in mobile devices like smartwatches and cell phones, and they are replacing LCDs in other devices. OLED displays come as monochrome or RGB colour devices. Small OLED display modules usually have an onboard control circuit that uses digital interfaces like I2C or SPI.

 OLED I2C display
Figure 92: OLED I2C display
 Arduino and OLED I2C schematics
Figure 93: Arduino and OLED I2C schematics
//Add libraries to ensure the functioning of OLED
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
 
void setup() {
  //Setting up initial OLED parameters
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false);
  display.setTextSize(1); //Size of the text
  display.setTextColor(WHITE); //Colour of the text – white
 
void loop() {
 
  //Print out on display output sensor values
  display.setCursor(0, 0);
  display.clearDisplay();
  display.print("Test of the OLED"); //Print out the text on the OLED
  display.display();
  delay(100);
  display.clearDisplay();
}

Electronic Ink Display (E-Ink)

E-ink display uses charged particles to create a paper-like effect. The display comprises transparent microcapsules filled with oppositely charged white and black particles between electrodes. Charged particles change their location depending on the orientation of the electric field; thus, individual pixels can be either black or white. The image does not need power to persist on the screen; power is used only when the image is changed. Thus e-ink display is very energy efficient. It has high contrast and viewing angle but a low refresh rate. E-ink displays are commonly used in e-readers, smartwatches, outdoor signs, and electronic shelf labels.

 E-ink display module
Figure 94: E-ink display module
 Arduino Uno and E-ink display module schematics
Figure 95: Arduino Uno and E-ink display module schematics
#include <SmartEink.h>
#include <SPI.h>
 
E_ink Eink;
 
void setup()
{
  //BS LOW for 4 line SPI
  pinMode(8,OUTPUT);
  digitalWrite(8, LOW);
 
Eink.InitEink();
 
Eink.ClearScreen();//Clear the screen
 
Eink.EinkP8x16Str(14,8,"IoT e-ink example");
Eink.EinkP8x16Str(10,8,"IoT-open.eu");
Eink.EinkP8x16Str(6,8,"0123456789");
Eink.EinkP8x16Str(2,8,"9876543210");
 
Eink.RefreshScreen(); 
}
void loop()
{ 
 
}

[pczekalski]Add paper colourfully technologies

Electromechanical Devices

Relay

Relays are electromechanical devices that use electromagnets to connect or disconnect the plates of a switch. Relays are used to control high-power circuits with low-power circuits. Circuits are electrically isolated and thus protect logic control. Relays are used in household appliance automation, lighting and climate control. Although the electromagnet's coil of the relay requires relatively low power compared to the power capability of the output circuit, it cannot be connected directly to the microcontroller's pin. Creating the transistor driver or using a relay module with the driver built-in is possible.

 1 channel relay module
Figure 96: 1 channel relay module
 Arduino Uno and 1 channel relay module schematics
Figure 97: Arduino Uno and 1 channel relay module schematics

The example code:

#define relayPin  4 //Define the relay pin
 
void setup()
{    
  Serial.begin(9600);
  pinMode(relayPin, OUTPUT); //Set relayPin to output
 
}
 
void loop()
{
   digitalWrite(relayPin,0);  //Turn relay on
   Serial.println("Relay ON"); //Output text 
   delay(2000); // Wait 2 seconds
 
   digitalWrite(relayPin,1);  //Turns relay off
   Serial.println("Relay OFF");
   delay(2000);
}

Solenoid

Solenoids are devices that use electromagnets to pull or push iron or steel core. They are used as linear actuators for locking mechanisms indoors, pneumatic and hydraulic valves and in-car starter systems.

Solenoids and relays both use electromagnets, and connecting them to Arduino is very similar. Coils need a lot of power, and they are usually attached to the circuit's power source with the use of a transistor driver. Turning the coil's power off makes the electromagnetic field collapse and creates a very high voltage. A shunt diode is used to channel the overvoltage for the semiconductor devices' protection. For extra safety, an optoisolator can be used.

 Solenoid
Figure 98: A solenoid
Figure 99: Arduino Uno and solenoid schematics

The example code:

#define solenoidPin  4 //Define the solenoid pin
 
void setup()
{    
  Serial.begin(9600);
  pinMode(solenoidPin, OUTPUT); //Set solenoidPin to output
 
}
 
void loop()
{
   digitalWrite(solenoidPin,0);  //Turn solenoid on
   Serial.println("Solenoid  ON"); //Output text 
   delay(2000); //Wait 2 seconds
 
   digitalWrite(solenoidPin,1);  //Turns solenoid off
   Serial.println("Solenoid OFF");
   delay(2000);
}

Sound Output Devices

Speaker

Speakers are electroacoustic devices that convert electrical signals into sound waves. A speaker uses a permanent magnet and a coil attached to the membrane. Sound signal, flowing through the coil, creates the electromagnetic field with variable strength; coil attracts to magnet according to the strength of the field, thus making a membrane vibrate and creating a sound wave. Another widely used speaker technology, called piezo speaker, uses piezoelectric materials instead of magnets. Speakers are used to create an audible sound for human perception and ultrasonic sound for sensors and measurement equipment. Some speakers are created to generate acoustic tones of a single, fixed frequency. Such elements are called buzzers and have a built-in generator to emit sound if the voltage is on. Elements without built-in generators should be controlled with the frequency signal coming from the microcontroller. Sound-generating devices require more power than LED, so there is a need to check if the operating current is lower than the maximum current of the microcontroller's pin as specified in technical documentation. If yes, the additional amplifying element is required, e.g. transistor.

 Electromagnetic speaker
Figure 100: Electromagnetic speaker, 8 Ω 0.5 W
 Arduino Uno and piezo speaker schematics
Figure 101: Arduino Uno and piezo speaker schematics
const int speakerPin = 9;      //Define the piezo speaker pin
 
void setup()
{
  pinMode(speakerPin, OUTPUT); //Set spekaer pin as an output
}
 
void loop()
{ 
  tone(speakerrPin, 1000);     //Send 1 kHz sound signal
  delay(1000);                 //For 1 s
  noTone(speakerPin);          //Stop sound
  delay(1000);                 //For 1 s
}

Actuators

Actuators are devices that can do a physical action to the surrounding world. Most actuators are based on one of the forms of electric motors, sometimes directly, sometimes using a gearbox and advanced control logic.
An electric motor is an electromechanical device which can turn electrical energy into mechanical energy. The motor turns because the electricity that flows in its winding generates a magnetic field that inducts the mechanical force between the winding and the magnet. Electric motors are made in many variants, of which the simplest is the permanent-magnet DC motor.

DC Motor (One Direction)

DC motor is a device which converts direct current into mechanical rotation. DC motor consists of permanent magnets in the stator and coils in the rotor. Applying the current to coils creates an electromagnetic field, and the rotor tries to align itself to the magnetic field. Each coil is connected to a commutator, which in turn supplies coils with current, thus ensuring continuous rotation. Some motors have a tachometer functionality as the loopback signal that generates a pulse train of frequency proportional to the rotation speed. Tacho signal can be connected to a digital or interrupt input of a microcontroller allowing for determining actual rotation speed. DC motors are widely used in power tools, toys, electric cars, robots, etc.

 A DC motor with gearbox
Figure 102: A DC motor with gearbox 50:1
 Arduino Uno and DC motor schematics
Figure 103: Arduino Uno and DC motor schematics
void setup ()
{
  pinMode(5,OUTPUT); //Digital pin 5 is set to output
  //The function for turning on the motor is defined
  #define motON digitalWrite(5,HIGH) 
  //The function for turning off the motor is defined
  #define motOFF digitalWrite(5,LOW)  
}
void loop ()
{
  motON; //Turn on the motor
}

DC Motor With H-Bridge

The H-bridge has earned its name because of its resemblance to the capital ‘H’ wherein all the corners are switches, and the electric motor is in the middle. This bridge is usually used for operating permanent-magnet DC motors, electromagnets and other similar elements because it allows operating with significantly bigger current devices using a small current. By switching the switches, it is possible to change the motor direction. It is important to remember that the switches must be turned on and off in pairs.

 The flow of currents in the H-bridge
Figure 104: The flow of currents in the H-bridge

When all of the switches are turned off, the motor is in free movement. It is not always acceptable, so two solutions can be implemented. If both positive or negative switches are turned on at the top or at the bottom, then the motor coil is shorted, not allowing it to have a free rotation – it is slowed down faster. The fastest option to stop the motor is to turn the H-bridge in the opposite direction for some period of time. Remember! Neither of these braking mechanisms is good for the H-bridge or the power source because of excessive current appearance. That is why this action is unacceptable without a particular reason because it can damage the switches or the power source. The motor management can be reflected in the table.

Table 7: The Management of the H-Bridge Switches
Upper left Upper right Lower left Lower right Motor work mode
On Off Off On Turns in one direction
Off On On Off Turns in another direction
On On Off Off Braking
Off Off On On Braking

The complicated part is the realisation of switches, usually implemented as relays or appropriate power transistors. The biggest drawback of relays is that they can only turn the engine on or off. Transistors must be used if the rotation speed needs to be regulated using the pulse width modulation. The MOSFET-type transistors should be used to ensure a large amount of power. Nowadays, the stable operation of the bridge is ensured by adding extra elements. All elements can be encapsulated in a single integrated circuit, e.g. L293D.

 The L293D microchip and its representation in the circuit
Figure 105: The L293D microchip and its representation in the circuit

The L293D microchip consists of two H-bridges and is made for managing two motors. It has separate control pins for the left and right branches avoiding the power short circuit if connected properly. An example schematic diagram of connecting the chip to the Arduino Uno board can be seen in the following figure. Notice that using a PWM signal allows to control the rotation speed.

 Arduino Uno and L293D H-bridge schematics
Figure 106: Arduino Uno and L293D H-bridge schematics

The example code:

int dirPin1 = 7;    //1st direction pin
int dirPin2 = 8;    //2nd direction pin
int speedPin = 5;   //Pin responsible for the motor speed
 
void setup ()
{
  pinMode (dirPin1,OUTPUT);  //1st direction pin is set to output
  pinMode (dirPin2,OUTPUT);  //2nd direction pin is set to output
  pinMode (speedPin,OUTPUT); //Speed pin is set to output
}
 
void loop ()
{
  analogWrite(speedPin, 100); //Setting motor speed
  //Speed value can be from 0 to 255
 
  int motDirection = 1; //Motor direction can be either 0 or 1
 
  if (motDirection) //Setting motor direction
  {//If 1
    digitalWrite(dirPin1,HIGH);
    digitalWrite(dirPin2,LOW);
  }
  else
  {//If 0
    digitalWrite(dirPin1,LOW);
    digitalWrite(dirPin2,HIGH);
  }
}
Linear actuator

A bidirectional DC motor, usually controlled with an H-bridge and equipped with thread gear, can be used to implement the linear actuators.
Linear actuators used to be equipped with end position detectors such as switches, or eventually, their end positions can be detected with overload detection. A simple linear actuator is present in figure 107.

 Low voltage linear actuator
Figure 107: Low voltage linear actuator

Stepper Motor

Stepper motors can be moved by a certain angle or step. The full rotation of the motor is divided into small, equal steps. Stepper motor has many individually controlled electromagnets; turning them on or off makes a motor shaft rotate by one step. Changing the switching speed or order can precisely control the rotation's angle, direction or speed. Because of their exact control ability, they are used in CNC machines, 3D printers, scanners, hard drives etc. An example of use can be found in the source [55].

 A stepper motor
Figure 108: A stepper motor
 Arduino Uno and stepper motor schematics
Figure 109: Arduino Uno and stepper motor schematics

The example code:

#include <Stepper.h> //Include library for stepper motor
 
int in1Pin = 12; //Defining stepper motor pins
int in2Pin = 11;
int in3Pin = 10;
int in4Pin = 9;
 
//Define a stepper motor object
Stepper motor(512, in1Pin, in2Pin, in3Pin, in4Pin);  
 
void setup()
{
  pinMode(in1Pin, OUTPUT); //Set stepper motor control pins to output
  pinMode(in2Pin, OUTPUT);
  pinMode(in3Pin, OUTPUT);
  pinMode(in4Pin, OUTPUT);
 
  Serial.begin(9600);
  motor.setSpeed(20); //Set the speed of stepper motor object
}
 
void loop()
{
    motor.step(5); //Rotate 5 steps
}

Servomotor

The servomotor includes the internal closed-loop position feedback mechanism that precisely controls its position angle. To set the angle, the PWM technique is used. Additionally, it is possible to control the speed of angle change, acceleration and deceleration of the rotation. Servomotors have limited rotation angles depending on their type, e.g. 90, 180 or 270 degrees. A typical servo is 180 degrees (usually a bit lower). Servo powering depends on size; micro servos are typically between 4.8V and 6V. Larger servos require higher voltage and more current to operate. There are two standards for controlling servos, so-called “analogue” and “digital”. Analogue servos are controlled with a PWM signal of 50Hz (20ms period), while digital servos, even if backwards compatible with analogue, can be controlled with a PWM signal up to 250Hz. A duty cycle (a ratio between HIGH and LOW) controls servo position. We focus below on analogue servomotors, controlled with a 50Hz PWM signal, but the transition to digital ones is straightforward. Note digital servos used to have individual configurations of the control signals, and it is necessary to refer to the documentation of the particular model for correct timings. From the image 110, it can be seen that the length of the servomotor impulse cycle is 20 ms, but the impulse length itself is 1 ms or 2 ms. These signal characteristics are true for most enthusiast-level servomotors but should be verified for each module in the manufacturer specification, e.g. to obtain a full rotation of 180 degrees, it may be necessary to go beyond standard 1ms↔2ms duty cycle. The servomotor management chain meets the impulse every 20 ms, but the pulse width shows the position that the servomotor has to reach. For example, 1 ms corresponds to the 0° position but 2 ms – to the 180° position against the starting point. When entering the defined position, the servomotor will keep it and resist any outer forces that are trying to change the current position. The graphical representation is in the image 110.

 The pulse width modulated signal for different positions of servomotor
Figure 110: The pulse width modulated signal for different positions of servomotor

Just like other motors, servomotors have different parameters, where the most important one is the time of performance – the time that is necessary to change the position to the defined position. The best enthusiast-level servomotors do a 60° turn in 0.09 s. There are three types of servomotors:

  • positional rotation servomotor – most widely used type of servomotor. With the help of a management signal, it can determine the position of the rotation angle from its starting position;
  • continuous rotation servomotor – this type of motor allows setting the speed and direction of the rotation using the management signal. If the position is less than 90°, it turns in one direction, but if more than 90°, it turns in the opposite direction. The speed is determined by the difference in value from 90°; 0° or 180° will turn the motor at its maximum speed while 91° or 89° at its minimum rate;
  • linear servomotor – with the help of additional transfers, it allows moving forward or backwards; it doesn’t rotate.

Unfortunately, using Arduino, the servomotor is not as easily manageable as the DC motor. For this purpose, a special servomotor management library Servo.h has been created. Using PWM signal in other MCUs may involve the use of hardware or software timers and may impact other features as the number of hardware timers used to be limited. Thus Servo.h implementation may vary between microcontrollers and SDKs.

 A servomotor
Figure 111: A servomotor
 Arduino Uno and servomotor schematics
Figure 112: Arduino Uno and servomotor schematics

The example code:

#include <Servo.h> //Include Servo library
Servo servo; //Define a Servo object
 
void setup ()
{
  servo.attach(6); //Connect servo object to pin D6
  servo.write(90); //Set position of servo to 90°
  Serial.begin(9600);
}
 
void loop ()
{
  servo.write(110); //Set position of servo to 110°
  delay(200); //wait for 200 ms
  servo.write(70);//Set position of servo to 70°
  delay(200); //Wait for 200 ms
}

/data01/virt87891/domeenid/www.robolabor.ee/htdocs/homelab/data/pages/en/iot-open/hardware2/powering.txt— MISSING PAGE — /data01/virt87891/domeenid/www.robolabor.ee/htdocs/homelab/data/pages/en/iot-open/networking2.txt— MISSING PAGE — /data01/virt87891/domeenid/www.robolabor.ee/htdocs/homelab/data/pages/en/iot-open/iotprogramming2.txt— MISSING PAGE — /data01/virt87891/domeenid/www.robolabor.ee/htdocs/homelab/data/pages/en/iot-open/frameworkstools2.txt— MISSING PAGE —

Notes for Further Studying

It is worth nothing to mention that even every second, there grow new IoT ideas, hardware, software and applications. Because of that, technical, specific knowledge, mostly on hardware and software, becomes rapidly outdated. Moreover, due to the amount of information related to embedded systems development and IoT development, it is impossible to assemble all information regarding the IoT world.

The IOT-OPEN.EU project is instantly evolving and always brings new content, but it cannot be the only source of knowledge in the current stage of development. We distribute all content via a single starting point, the website http://iot-open.eu, but we suggest navigating to the online resources presented below. Those projects, websites and resources are not related to our project. Still, we consider them a valuable source.

Please also note even if the IOT-OPEN.EU project is CC BY-NC licenced, resources juxtaposed below may need an access fee, registration and so on.

Many online platforms provide online courses by different universities about relevant topics like the Internet of Things, embedded systems, programming languages, connectivity and security, robotics, big data, computer vision and many more. Some of the most popular platforms are Coursera [56], edX [57], Udacity [58], Udemy [59], Skillshare [60]. Some of these courses are free of charge, and at the end of these courses, a certificate about skills can be acquired (often for an additional price).

Electronics Tutorials website [61] offers multiple basic electronics tutorials topics, including AC and DC circuit theory, amplifiers, semiconductors, filters, Boolean algebra, capacitors, power electronics, transistors, operational amplifiers, sequential logic, and many more. It contains an extensive description of the theory with graphics and explanations.

Embedded Experts website [62] focuses on commercial, certified courses mainly related to the embedded platforms. It may be helpful for studying technologies related to the Edge Class and Fog Class devices that are fundamental for IoT and bare metal IoT development.

Instructables [63] is a project platform with plenty of Internet of Things projects for different knowledge levels. It is also possible to enrol on other classes with many lessons that teach about specific related topics that are not limited only to electronics but also cover issues such as sewing, food, craft, 3D printing, etc. One section of the Instructables website offers multiple contests and challenges about the related topic with valuable prizes.

Tinkercad is a simple, online 3D design and 3D platform that also allows to model and test circuits [64]. With Tinkercad, it is possible to program and simulate a virtual Arduino board online using different libraries and serial monitors. There are also plenty of already existing starter examples.

Wokwi [65] is an online (in-browser) IoT device simulator. You can implement some simple and limited approaches still going beyond embedded systems. Note it is not a distant lab (such as our IOT-OPEN.EU VREL lab [66]) but rather a software simulation of the IoT hardware development boards, so your experience will be limited.

Authors

IOT-OPEN.EU Reloaded Consortium partners proudly present the 2nd edition of the Introduction to the IoT book. The complete list of contributors is juxtaposed below.

ITT Group

  • Raivo Sell, Ph. D., ING-PAED IGIP
  • Rim Puks, Eng.
  • Mallor Kingsepp, Eng.

Riga Technical University

  • Agris Nikitenko, Ph. D., Eng.
  • Karlis Berkolds, M. sc., Eng.
  • Anete Vagale, M. sc., Eng.
  • Rudolfs Rumba, M. sc., Eng.

Silesian University of Technology

  • Piotr Czekalski, Ph. D., Eng.
  • Krzysztof Tokarz, Ph. D., Eng.
  • Oleg Antemijczuk, M. sc., Eng.
  • Jarosław Paduch, M. sc., Eng.
  • Godlove Suila Kuaban, M. sc., Eng.

Tallinn University of Technology

  • Raivo Sell, Ph. D., ING-PAED IGIP
  • Karl Läll, B. sc., Eng.

SIA RobotNest

  • Karlis Berkolds, M. sc., Eng.

IT Silesia

  • Łukasz Lipka, M. sc., Eng.

University of Messina

  • Salvatore Distefano
  • Rustem Dautov
  • Riccardo Di Pietro
  • Antonino Longo Minnolo

ITMO University

  • Aleksandr Kapitonov, Ph. D., Assoc. Prof.
  • Dmitrii Dobriborsci, M. sc., Eng.
  • Igor Pantiukhin, M. sc., Eng.
  • Valerii Chernov, Eng.

Graphic Design and Images

  • Blanka Czekalska, M. sc., Eng., Arch.
  • Piotr Czekalski, Ph. D., Eng.

Reviewers (1st edition)

  • Fabio Bonsignorio, Ph. D., Eng.– Professor at Scuola Superiore Sant'Anna, Institute of Biorobotics
  • Artur Pollak, M. sc., Eng. – CEO at APAGroup
  • Ivars Parkovs, M. sc., Eng. – R&D Senior Engineer at “SAF Tehnika” Ltd.
  • Janis Lacaunieks, M. sc., Eng. – R&D Engineer at “SAF Tehnika” Ltd.

Versions

This page keeps track of the content reviews and versions done as a continuous maintenance process

Table 8: Versions and Content Updates
Version Update Date Content updates summary Other comments
1 v 0.1 01.05.2023 Preliminary version
2 v 0.2 02.05.2023 Authors, Preface and Versions Authors to be updated with consortium partners
3 v 0.25 08.05.2023 Authors, Preface, Project Information Authors to be updated with consortium partners
4 v 0.26 18.06.2023 Introduction chapter revised
5 v 0.27 28.06.2023 Updated contents of the Mobility (Fog, edge), enabling technologies, IoT definition
6 v 0.28 28.06.2023 Introduction to the Embedded Programming updated: exposed edge class devices and modified Scripting section for Fog devices. Introduction to the Programming Frameworks updatd to expose Edge class and refer to fog.
7 v 0.29 28.06.2023 Updated Analogue section in embeddedcommunicationprotocols
8 v.0.3 19.08.2023 Reviewed and revised Actuators
9 v.0.31 20.08.2023 Updated hardware specific extensions for C++ programming
10 v.0.32 21.08.2023 Implemented PWM

Preface

This book and its offshoots were prepared to provide comprehensive information about the Internet of Things on the engineering level.
Its goal is to introduce IoT to bachelor students, master students, technology enthusiasts and engineers willing to extend their current knowledge with the latest hardware and software achievements in the scope of the Internet of Things.
This book is also designated for teachers and educators willing to prepare a course on IoT.

We (Authors) assume that persons willing to study this content possess some general knowledge about IT technology, i.e. understand what an embedded system is, know the general idea of programming (in C/C++) and are aware of wired and wireless networking as it exists nowadays.

This book constitutes a comprehensive manual for IoT technology; however, it is not a complete encyclopedia nor exhausts the market. The reason for it is pretty simple – IoT is so rapidly changing technology that new devices, ideas and implementations appear daily. Once you read this book, you can quickly move over the IoT environment and market, easily chasing ideas and implementing your IoT infrastructure.

We also believe this book will help adults that took their technical education some time ago to update their knowledge.

We hope this book will let you find brilliant ideas in your professional life, see a new hobby, or even start an innovative business.

Playing with real or virtual hardware and software is always fun, so keep going!

Project Information

ThisBook was implemented under the Erasmus+ KA2 projects:

  • Strategic Partnerships in the Field of Education, Training, and Youth – Higher Education, 2016, IOT-OPEN.EU – Innovative Open Education on IoT: Improving Higher Education for European Digital Global Competitiveness, project number: 2016-1-PL01-KA203-026471,
  • Cooperation Partnerships in higher education, 2022, IOT-OPEN.EU Reloaded: Education-based strengthening of the European universities, companies and labour force in the global IoT market, project number: 2022-1-PL01-KA220-HED-000085090.

Erasmus+ Disclaimer
This project has been funded with support from the European Commission.
This publication reflects the views only of the author, and the Commission cannot be held responsible for any use which may be made of the information contained therein.

Copyright Notice
This content was created by the IOT-OPEN.EU Consortium: 2016–2019 and IOT-OPEN.EU Reloaded Consortium 2022-2025.
The content is Copyrighted and distributed under CC BY-NC Creative Commons Licence, free for Non-Commercial use.

CC BY-NC

In case of commercial use, please contact IOT-OPEN.EU Reloaded Consortium representative.

Introduction

Here comes the Internet of Things. The name that recently makes red-hot people in business, researchers, developers, geeks and … students. The name that non-technology related people consider a kind of magic and even a danger to their privacy. The EU set the name as one of the emerging technologies and estimated the worldwide market will hit well over 500 billion US dollars in 2022, while the number of IoT devices in 2030 is expected to be over 3.2 billion.

What is IoT (Internet of Things), then? Surprisingly, the answer is not straightforward.

Color coding

To simplify the selection of different topics, a simple colour coding was introduced, indicating the skills required to cover particular topics. Colour codes are organized in the form of colour bars enclosing chapter titles.

Explanation:

This chapter is for beginners (including amateurs) or higher

This chapter is for bachelor students of higher

This chapter is for master students of higher

Definition of IoT

Let us roll back to the 1970s first. In 1973 the first RFID device was patented. This device was the key enabling technology even if it does not look nor remind modern IoT devices. The low power (actually here passive) solution with a remote antenna large enough to collect energy from the electromagnetic field and power the device brought an idea of uniquely identifiable items. That somehow mimics well-known EAN barcodes and the evolution used nowadays, like QR codes, but every single thing has a different identity here. In contrast, EAN barcodes present a class of products, not an individual one. The possibility to identify a unique identity remotely became fundamental to the IoT as it's known today. RFID is not the only technology standing behind IoT. In the 1990s, the rapid expansion of wireless networks, including broadband solutions like cellular-based data transfers with their consequent generations, enabled connecting devices in various, even distant geographical locations. Parallelly an exponential increase in the number of devices connected to the global Internet network was observed, including the smartphone revolution that started around mid the first decade of the XXI century. On the hardware level, microchips and processors became physically smaller and more energy efficient yet offering growing computing capabilities and memory size increase, along with significant price drops. All those facts drove the appearance of small, network-oriented, cheap and energy-efficient electronic devices. In recent years, the development of efficient AI technologies even boosted IoT applications.

What is IoT?

The phrase “Internet of Things” was used for the first time in 1999 by Kevin Ashton – an expert on digital innovation. Formally IoT was introduced by the International Telecommunication Union (ITU) in the ITU Internet report in 2005 [67]. The understanding and definitions of IoT changed over the years, but now all agree that this cannot be seen as a technology issue only. According to IEEE “Special Report: Internet of Things” [68] released in 2014, IoT is:

IEEE Definition of IoT
A network of items – each embedded with sensors – connected to the Internet.

It relates to the physical aspects of IoT only. The Internet of Things also addresses other aspects that cover many areas [69]:

  • enabling technologies,
  • software,
  • applications and services,
  • business models,
  • social impact,
  • security and privacy aspects.

IEEE, as one of the most prominent standardisation organisations, also works on standards related to the IoT. The primary document is IEEE P2413™ [70]. It covers the technological architecture of IoT as three-layered: sensing at the bottom, networking and data communication in the middle, and applications on the top. It is essential to understand that IoT systems are not only small, local-range systems. ITU-T has defined IoT as:

ITU-T Definition of IoT
A global infrastructure for the information society, enabling advanced services by interconnecting (physical and virtual) things based on existing and evolving interoperable information and communication technologies.

In the book [71] by European Commission, we can read a similar description of what IoT is: “The IoT is the network of physical objects that contain embedded technology to communicate and sense or interact with their internal states or the external environment.” IoT impacts many areas of human activity: manufacturing, transportation, logistics, healthcare, home automation, media, energy saving, environment protection and many more. In this course, we will consider the technical aspects mainly.

"Thing"

In the IoT world, the “thing” is always equipped with some electronic element that can be as simple as the RFID tag, an active sensor sending data to the global network, or an autonomous device that can react to environmental changes. In CERP-IoT book “Visions and Challenges” [72] in the context of “Internet of Things” a “thing” could be defined as:

CERP-IoT Definition of “Thing”
A real/physical or digital/virtual entity that exists and moves in space and time and can be identified. Assigned identification numbers, names and location addresses commonly identify things.

It is quite easy to find other terms used in the literature like “smart object”, “device”, or “nodes” [73].

Passive Thing

One can imagine that almost everything in our surroundings is tagged with an RFID element. They do not need a power supply; they respond with a short message, usually containing the identification number. Modern RFID can achieve 6 to 7 meters of the range. Using the active RFID reader, we can quickly locate lost keys and know if we still have the butter in the fridge and in which wardrobe there is our favourite t-shirt.

Active Thing

If the “thing” includes the sensor, it can send interesting data about current conditions. We can sense environmental parameters like temperature, humidity, air pollution, pressure, localisation data, water level, light, noise, and movement. Using different methods and protocols, this data can be sent to the central collector that connects to the Internet and the database or cloud. There the data can be processed, and Artificial Intelligence algorithms can be used to decide actions that could be taken in different situations. Active things can also receive control signals from the central controller to control the environment: turn on/off the heating or light, water flowers, and turn on the washing machine when there is enough sunlight to generate the required electricity or charge your electric car.

Autonomous Thing

This thing does not even require the controller to realise the proper decision. An autonomous vacuum cleaner can clean our house when it detects that we aren't home and the floor needs cleaning. The fridge can order our favourite beverage once the last bottle is almost empty.

Sensor Network

Sensor Networks are a subset of the IoT devices used as a collaborative solution to grab data and send it for further processing. Opposite to the general IoT devices, Sensor Network devices do not have any actuators that can apply an action to the external world. The data flow is unidirectional, then.

IoT vs Embedded Systems

IoT systems and embedded systems share almost the same domain. They frequently use the same microcontrollers, sensors and actuators, development software and even programming models. What differs between IoT and embedded systems is that IoT, on its principles, uses communication to send and receive data outside of its instance, while embedded systems do not have to. Embedded systems do not have to be network-enabled, and they do not have a unique identity frequently, while IoT devices do. Moreover, IoT systems are complex and multilayered, often introducing cloud-based parts, while embedded systems are stand-alone devices. Shortly we can say that an IoT device is network enabled embedded system.

Enabling Technologies

In this chapter, there is an approach to describe modern technologies that appeared in the last few years, enabling the idea of IoT to be widely implementable. In the [74] one can read that “The confluence of efficient wireless protocols, improved sensors, cheaper processors and a wave of startups and established companies made the concept of the IoT mainstream”. Similar analysis has been done in [75] where authors write that “the latest developments in RFID, smart sensors, communication technologies and Internet protocols enable the IoT”. RFID and smart sensors need the microprocessor system to read, convert the data into digital format, and send it to the Internet using the communication protocol. This process can be done by small- and medium-scale computer (embedded) systems. These are essential elements of technologies used in IoT systems.

Edge class devices

In recent years one can observe rapid growth in the field of microprocessors. It includes not only the powerful desktop processors but also microcontrollers – elements that are used in small-scale embedded systems. We can also notice the popularity of microprocessor systems that can be easily integrated with other factors, like sensors, and actuators, connected to the network. Essential is also the availability of programming tools and environments supported by different companies and communities. An excellent example of such a system is Arduino. Those devices are low-power, constrained devices, usually battery-powered and, in most cases, communicating wirelessly.

Fog class devices

The same growth can be observed in the advanced constructions comparable to low-end computers. They have more powerful processors, memory and networking connectivity built-in than small-scale computer systems. They can work under the control of multitasking operating systems like Linux and Windows and embedded or real-time operating systems like FreeRTOS. Having many libraries, they can successfully work as hubs for local storage, local controllers and gateways to the Internet. Raspberry Pi and the nVidia Jetson series are examples of such systems. This category of devices frequently contains hardware accelerated (such as GPU) AI-capable solutions, i.e. nVidia Jetson Nano or Xavier series. Those devices can be battery or mains powered. Often, they are green energy powered: i.e. with a larger backup battery and energy harvesting solution (such as solar panel).

Access to the Internet

Nowadays, the Internet is (almost) everywhere. There are lots of wireless networks available in private and public places. The price of cellular access (3G/4G/5G) is low, offering a suitable data transfer performance. Connecting the “thing” to the Internet has never been so easy.

IP Addressing Evolution

The primary paradigm of IoT is that every unit can be individually addressed. With the addressing scheme used in IPv4, it wouldn't be possible. IPv4 address space delivers “only” 4 294 967 296 of unique addresses (2^32). If you think it's a considerable number, imagine that every person in the world has one IP-connected device – IPv4 covers about half of the human population. The answer is IPv6 with a 128-bit addressing scheme that gives 3.4 × 10^38 addresses. It will be enough even if everyone has a billion devices connected to the Internet.

Data Storage and Processing

IoT devices generate the data to be stored and processed somewhere. If there is a couple of sensors, the amount of data is not very big, but if there are thousands of sensors generating data hundreds of times every second. The cloud can handle it – the massive place for the data with tools and applications ready to help with data processing. Some big, global clouds are available for rent, offering not only storage but also Business Intelligence tools, Artificial Intelligence analytic algorithms. There are also smaller private clouds created to cover the needs of one company only. Many universities have their own High-Performance Computing Centre.

Mobile Devices

Many people want to be connected to the global network everywhere, anytime, having their “digital twin” with them. It is possible now with small, powerful mobile devices like smartphones. Smartphones are also elements of the IoT world, being together sensors, user interfaces, data collectors, wireless gateways to the Internet, and everything with mobility features.

The technologies we mentioned here are the most recognisable. Still, there are many others, more minor, described only in the technical language in some standard description document, hidden under the colourful displays between large data centres, making our IoT world operable. In this book, we will describe some of them.

A special note on Fog class and Edge class devices

Technology development instantly shifts devices between categories. A border between Fog and Edge class devices is conventional; many can share both worlds. It depends on their purpose, application and performance configuration; thus, i.e. Raspberry Pi can be an end-node (Edge) class device and a Fog class, working as a data aggregator and analytical device.

Mobility – New Paradigm for IoT Systems

IoT has already been defined as a network of physical things or devices that might include sensors or simple data processing units, complex actuators, and significant hybrid computing power. Today IoT systems have transitioned from being perceived as sensor networks to smart-networked systems capable of solving complex tasks in mass production, public safety, logistics, medicine and other domains, requiring a broader understanding and acceptance of current technological advancements, including advanced data processing that includes AI.

Since the very beginning of sensor networks, one of the main challenges has been data transport and data processing, where significant efforts have been put by the ICT community towards service-based system architectures. However, The current trend already provides considerable computing power even in small mobile devices. Therefore, the concepts of future IoT already shifted towards smarter and more accessible IoT devices, and data processing has become possible closer to the Fog and Edge.

Cloud Computing

Cloud-based computing is a relatively well-known and adequately employed paradigm where IoT devices can interact with remotely shared resources such as data storage, data processing, data mining and other services are unavailable to them locally because of the constrained hardware resources (CPU, ROM, RAM) or energy consumption limits. Although the cloud computing paradigm can handle vast amounts of data from IoT clusters, the transfer of extensive data to and from cloud computers presents a challenge due to limited bandwidth[76]. Consequently, there is a need to process data near data sources, employing the increasing number of smart devices with enormous processing power and a rising number of service providers available for IoT systems.

Fog Computing

Fog computing addressed the bottlenecks of cloud computing regarding data transport while providing the needed services to IoT systems. It is a new trend in computing that aims to process the data near the data source. Fog computing pushes applications, services, data, computing power, and decision-making away from the centralised nodes to the logical extremes of a network. Fog computing significantly decreases the data volume that must be moved between end devices and the cloud. Fog computing enables data analytics and knowledge generation at the data source. Furthermore, the dense geographic distribution of fog helps to attain a better-localised accuracy for many applications than the cloud processing of the data [77].
The recent development of energy-efficient hardware with AI acceleration enters the fog class of the devices, putting Fog Computing in the middle of the interest of IoT application development and extending new horizons to them. Fog Computing is more energy efficient than raw data transfer to the cloud and back, and in the current scale of the IoT devices, the application is meant for the future of the planet Earth. Fog Computing usually also brings a positive impact on IoT security, i.e. sending to the cloud preprocessed and depersonalised data and providing distributed computing capabilities that are more attack resistant.

Edge Computing

Recent development in hardware, power efficiency and a better understanding of the IoT data nature, including such aspects as, i.e. privacy and security, led to solutions where data is being processed and preprocessed right to their source in the Edge class devices. Edge data processing on end-node IoT devices is crucial in systems where privacy is essential and sensitive data is not to be sent over the network (i.e. biometric data in a raw form). Moreover, distributed data processing can be considered more energy efficient in some scenarios where, i.e. extensive, power-consuming processing can be performed during green energy availability.

Cognitive IoT Systems

According to [78], Cognitive IoT, besides a proper combination of hardware, sensors and data transport, comprises cognitive computing, which consists of the following main components:

  • understanding – in the case of IoT, it means systems' capability to process a significant amount of structured and unstructured data, extract the meaning of the data – produce a model that binds data to reality;
  • reasoning – involves decision-making according to the understood model and acquired data;
  • learning – creating new knowledge from the existing, sensed data and elaborated models.

Usually, cognitive IoT systems or C-IoT are expected to add more resilience to the solution. Resilience is a complex term and is differently explained under different contexts; however, there are standard features for all resilient systems. As a part of their resilience, C-IoT should be capable of self-failure detection and self-healing that minimises or gradually degrades the system's overall performance. In this respect, the non-resilient system fails or degrades in a step-wise manner. In case of security issues, that system should be able to change its security keys, encryption algorithms and take other measures to cope with the detected threats. Self-optimisation abilities are often considered part of the C-IoT feature list to provide more robust solutions. Recent development in the Fog and Edge class devices and the efficient software leverage cognitive IoT Systems to a new level.

All three approaches, from cloud to cognitive systems, focus on adding value to IoT devices, system users and related systems on-demand. Since market and technology acceptance of mobile devices is still growing, and the amount of produced data from those devices is growing exponentially, mobility as a phenomenon is one of the main driving forces of the technological advancements of the near future.

Notes for Further Studying

It is worth nothing to mention that even every second, there grow new IoT ideas, hardware, software and applications. Because of that, technical, specific knowledge, mostly on hardware and software, becomes rapidly outdated. Moreover, due to the amount of information related to embedded systems development and IoT development, it is impossible to assemble all information regarding the IoT world.

The IOT-OPEN.EU project is instantly evolving and always brings new content, but it cannot be the only source of knowledge in the current stage of development. We distribute all content via a single starting point, the website http://iot-open.eu, but we suggest navigating to the online resources presented below. Those projects, websites and resources are not related to our project. Still, we consider them a valuable source.

Please also note even if the IOT-OPEN.EU project is CC BY-NC licenced, resources juxtaposed below may need an access fee, registration and so on.

Many online platforms provide online courses by different universities about relevant topics like the Internet of Things, embedded systems, programming languages, connectivity and security, robotics, big data, computer vision and many more. Some of the most popular platforms are Coursera [79], edX [80], Udacity [81], Udemy [82], Skillshare [83]. Some of these courses are free of charge, and at the end of these courses, a certificate about skills can be acquired (often for an additional price).

Electronics Tutorials website [84] offers multiple basic electronics tutorials topics, including AC and DC circuit theory, amplifiers, semiconductors, filters, Boolean algebra, capacitors, power electronics, transistors, operational amplifiers, sequential logic, and many more. It contains an extensive description of the theory with graphics and explanations.

Embedded Experts website [85] focuses on commercial, certified courses mainly related to the embedded platforms. It may be helpful for studying technologies related to the Edge Class and Fog Class devices that are fundamental for IoT and bare metal IoT development.

Instructables [86] is a project platform with plenty of Internet of Things projects for different knowledge levels. It is also possible to enrol on other classes with many lessons that teach about specific related topics that are not limited only to electronics but also cover issues such as sewing, food, craft, 3D printing, etc. One section of the Instructables website offers multiple contests and challenges about the related topic with valuable prizes.

Tinkercad is a simple, online 3D design and 3D platform that also allows to model and test circuits [87]. With Tinkercad, it is possible to program and simulate a virtual Arduino board online using different libraries and serial monitors. There are also plenty of already existing starter examples.

Wokwi [88] is an online (in-browser) IoT device simulator. You can implement some simple and limited approaches still going beyond embedded systems. Note it is not a distant lab (such as our IOT-OPEN.EU VREL lab [89]) but rather a software simulation of the IoT hardware development boards, so your experience will be limited.

Data Management Aspects in IoT

Data management is a critical task in IoT. Due to the high number of devices (things) already available, that is tens of billions, and considering the data traffic generated by each of them through, i.e. sensor networks, infotainment (soft news) or surveillance systems, mobile social network clients, and so on, we are now even beyond the ZettaByte (ZB 2^70, 10^21 bytes) era. This opened up several new challenges in (IoT) data management, giving rise to data sciences and big data technologies. Such challenges have not to be considered as main issues to solve but also as significant opportunities fuelling the digital economy with new directions such as Cloudonomics [90] and IoTonomics, where data can be considered as a utility, a commodity to manage, curate, store, and trade appropriately. Therefore, properly managing data in IoT contexts is not only critical but also of strategic importance for business players as well as for users, evolving into prosumers (producers-consumers).

From a technological perspective, the main aspects of dealing with IoT data management are:

  • data source: data generation and production is a relevant part of IoT, involving sensors probing the physical system. In a cyber-physical-social system view, such sensors could be virtual (e.g. software) or even human (e.g. citizens, crowdsensing). The main issues in data production are related to the type and format of data, heterogeneity in measurements and similar issues. Semantics is the key to solving these issues, also through specific standards such as Sensor Web Enablement and Semantic Sensor Network;
  • data collection/gathering: once data are generated, these should be gathered and made available for processing. The collection process needs to ensure that the data gathered are defined and accurate so that subsequent decisions based on the findings are valid. Some types of data collection include census (data collection about everything in a group or statistical population), sample survey (collection method that includes only part of the total population), and administrative by-product (data collection is a byproduct of an organisation’s day-to-day operations). Usually, wireless communication technologies such as Zigbee, BlueTooth, LoRa, Wi-Fi and 3G/4G networks are used by IoT smart objects and things to deliver data to collection points;
  • filtering: is a specific preprocessing activity, usually performed at data source or data collector (IoT) nodes (e.g. motes, base stations, hotspots, gateways), aiming at cleaning noisy data, filtering noise and not helpful information;
  • aggregation/fusion: to reduce bandwidth before sending data to processing nodes, these are further elaborated, compressed, aggregated and fused (sensor/data fusion) to reduce the overall volume of raw data to be transmitted and stored;
  • processing: once data are adequately collected, filtered, aggregated, and fused, they can be processed. Processing can be both local and remote and usually includes preprocessing activities to prepare data for real processing. Local processing, when possible, is mainly tasked with a fast, lightweight computation on edges (Edge computing) and in the Fog layer, wherever possible, quickly providing results and local analytics. More complex computations are usually demanded to remote (physical or virtual) servers provided by local nodes (e.g. communication servers, cloudlets) in a Fog computing fashion or by Cloud providers as virtual machines hosted in data centres. This kind of computation can also involve historical data, providing global analytics, but hardly meets time-constrained applications and real-time requirements;
  • storage/archive: remote servers are also used for permanently storing and archiving data, making these available for further processing, even to third parties. The database is often used for that, mainly based on distributed, NoSQL key-store technologies to improve reliability and performance;
  • delivering/presentation/visualization: processing activity results must then be delivered to requestors and users. These have to be, therefore, adequately organised and formatted, ready for end-users. IoT data visualisation is becoming an integral part of the IoT. Data visualisation provides a way to display this avalanche of collected data in meaningful ways that clearly present insights hidden within this mass amount of information;
  • security and privacy: data privacy and security are among the most critical issues in IoT data management. Good results and reliable techniques for secure data transmission, such as TLS and similar, are available. This way, IoT data security issues mainly concern [91] securing IoT devices, since they are usually resource-constrained and therefore do not allow to adopt traditional cryptography scheme to data encryption/decryption. Data privacy and integrity should also be enforced in remote storage servers, anonymising data and allowing owners to properly manage (monitoring, removing) them while ensuring availability. Indeed, security and privacy issues vertically span the whole IoT stack. A promising technique to address IoT security issues, attracting growing interest from both academic and business communities, is blockchain [92].

IoT Application Domains

Application domains of the Internet of Things solutions are vast. Most prominent applications include (among others) [93]:

  • building and home automation,
  • smart water,
  • internet of food,
  • smart metering,
  • smart city (including logistics, retail, transportation),
  • industrial IoT,
  • precision agriculture and smart farming,
  • security and emergencies,
  • healthcare and wellness (including wearables),
  • smart environment,
  • energy management,
  • robotics,
  • smart grids.

Smart Homes are one of the first examples that come to mind when discussing Internet of Things domain applications. Smart home benefits include reduced energy wastage, the quality and reliability of devices, system security, reduced cost of basic needs, etc. Some home automation examples are environmental control systems that monitor and control heating, ventilation, air conditioning and sunscreens; electrical charging of vehicles; solar panels for electrical power and hot water; ambient lighting control, smart lighting for aquaria; home cooking and food ordering; access control (doors, garage, gate); smart plant irrigation systems (both indoors and outdoors); baby monitoring; timed pet food dispensers; monitoring perishable goods (for example, in the refrigerator); household items remote monitoring (for instance, of washer cycle status); tracking and proactive maintenance scheduling (such as, i.e. electric car charging); event-triggered task execution. Home security also plays a significant role in smart homes. Examples of applications are automatic door locks, sensors for opening doors and windows, pressure, motion and infrared sensors, security cameras, notifications about security (to the owner or the police) and fitness-related applications.

In Smart City, multiple IoT-based services are applied to different areas of urban settings. The aim of the smart city is the best use of public resources, improvement of the quality of resources provided to people and reduction of operating costs of public administration [94]. A smart city can include many solutions like smart buildings, smart grids for improving energy management, smart tourism, monitoring of the state of the roads and occupation of parking lots, public transportation optimisation, public safety, environment monitoring, automatic street lighting, signalling with smart power devices, control of water levels for hydropower or flood warnings, electricity-generating devices like solar panels and wind turbines, weather monitoring stations. Transportation in smart cities may include aviation, monitoring and forecasting of traffic slowdowns, timetables and current status, navigation and route planning, as well as vehicle diagnostics and maintenance reports, remote maintenance services, traffic accident information collection, fleet management using digital tachographs, smart parking, car/bicycle sharing services [95]. IoT in transportation makes cars interconnected, particularly in the approaching autonomous vehicles era.

Smart Grid is a digital power distribution system. This system gathers information using smart meters, sensors and other devices. After these data are processed, power distribution can be adapted accordingly. Smart grids deliver sustainable, economical and secure electricity supplies efficiently.

In Precision Agriculture and Smart Farming IoT solutions can be used to monitor the moisture of the soil and conditions of the plants, control microclimate conditions and monitor the weather conditions to improve farming [96]. The goal of using IoT in agriculture is maximising the harvest, reducing operational costs, being more efficient, and reducing environmental pollution using low-cost automated solutions. An interaction between the farmer and the systems can be done using a human-machine interface. In the future smart precision farming can be a solution for such challenges as increasing worldwide demand for food, a changing climate, and a limited supply of water and fossil fuels [97].

Internet of Food integrates many of the aforementioned techniques and encompasses different stages of the food delivery chain, including smart farming, food processing, transportation, storage, retail, and consumption. It provides more safety and improved efficiency at each food production and consumption stage, including reduced waste and increased transparency.

Similar to precision agriculture, which is part of IoT in industry, Smart Factories also tend to improve manufacturing by monitoring pollutant gas emissions, locating employees and with many other solutions.

Industrial IoT and smart factories are part of the Industry 4.0 revolution. In this model, modern factories can automate complex manufacturing tasks, thanks to the Machine-To-Machine communication model, which provides more flexibility in the manufacturing process to enable personalised, short-volume product manufacturing easily.

In the healthcare and wellness, IoT applications can monitor and diagnose patients and manage people and medical resources. It allows remote and continuous monitor the vital signs of patients to improve medical care and wellness of patients [98]. An essential part of smart welfare is wearables, including wristbands and smartwatches that monitor the activity level, heart rate and other parameters. Smart healthcare includes remote monitoring, care of patients, self-monitoring, smart pills, smart home care, Real-Time Health Systems (RTHS) and many more. Medical robotics can also be part of the healthcare IoT system that includes medical robots in precision surgery or distance surgery; some robots are used in rehabilitation and hospitals (for example, Panasonic HOSPI [99]) for delivering medication, drinks, etc. to patients.

Wearables used in IoT applications should be highly energy efficient, ultra-low power and small-sized. Wearables are installed with sensors and software for data and information collected about the user. Devices used in daily life like Fitbit [100] are used to track people's health and exercise progress in previously impossible ways, and smartwatches allow to access smartphones using this device on the wrist. But wearables are not limited only to wearing them on the wrist. They can also be glasses equipped with a camera, a sports bundle attached to the shoes or a camera attached to the helmet or as a necklace [101].

IoT Hardware Overview

IoT hardware infrastructure is mostly inheriting from the embedded systems of the SoC type. As IoT devices are by its nature network-enabled, many of the existing embedded platforms evolved towards network-enabled solutions, sometimes indirectly through delivering network processor (wired or wireless) as a peripheral device yet integrated on the development board (i.e. Arduino Uno with Ethernet Networking shield, GSM shield, etc.), sometimes a new system, integrating networking capabilities in one SoC (i.e. Espriff SoCs). More advanced devices that require OS to operate preliminarily benefited from externally connected peripheral network interfaces via common wired ports like USB (i.e. early versions of the Raspberry Pi, where WiFi card was delivered as USB stick), currently, usually integrate most of the network interfaces in a single board (i.e. RPi 3B, including Ethernet, WiFi and Bluetooth).

  • “4.1. Most Noticeable Platforms”;
  • “4.4. esp”;

Most Noticeable Platforms

IoT market is an emerging one. New hardware solutions appear almost daily, while others disappear quick. At the moment of writing this publication (2016–2019), there are some core hardware solutions that seem to be prominent for at least a couple of years, however. We've provided a short review of those platforms in the following sections:

  • AVR: Arduino – a development board that uses Atmel SoC, that is no doubt the most popular development platform for enthusiasts and professionals. Arduino itself barely offers networking capabilities yet; there is a vast number of extension boards including network interfaces (both wired and wireless);
  • ESP: Espriff (Espressif Systems) – the great SoC solution of the single SoC including wireless network interfaces;
  • ARM: Raspberry Pi (and its clones) – advanced boards, including Linux operating system with GUI interface, even able to replace desktop computers.

Embedded Systems Communication Protocols

Understanding the principals of communication are essential for further reading on hardware and programming. Most microcontrollers (including SoCs) can communicate in the protocols juxtaposed below right “out of the box”. Interfaces can be implemented in hardware or (recently) in software. Some microcontrollers may require an external, dedicated protocol converter (a chip or a module).

IoT systems are typically structured into three basic layers [102]. The lowest layer is the Perception (physical, acquisition) Layer, the intermediate is the Network Layer, and the higher is the Application Layer. The function of the perception layer is to keep contact with the physical environment. Devices working in this layer are designed as embedded systems. They include the microprocessor or microcontroller, memory, communication unit, and interfaces – sensors or actuators. Sensors are elements that convert a value of some physical parameter into an electrical signal, while actuators are elements that control environment parameters. Sensors and actuators are interfaced with the microcontroller using different connection types. This chapter describes some internal protocols used to communicate between microcontrollers and other electronic elements that can be named “embedded protocols”. Description of the protocols used for wire and wireless transmission between the perception layer and higher layers is present in communications_and_communicating_sut The embedded protocol that can be used in specific implementation depends mainly on the type of the peripheral element. Some of them use an analogue signal that the microcontroller must convert to digital internally, some directly implement digital communication protocol.

Analog

Simple sensors do not implement the conversion and communication logic, and the output is just the analogue signal – voltage level depending on the value of the parameter that is measured. It needs to be further converted into a digital representation; this process can be made by analogue to digital converters (ADC) implemented as the internal part of a microcontroller or separate integrated circuit. Examples of the sensors with analogue output are a photoresistor, thermistor, potentiometer, resistive touchscreen.

Digital

Dummy, true/false information can be processed via digital I/O. Most devices use positive logic, where, i.e. +5 V (TTL) or +3.3 V (those are the most popular, yet there do exist other voltage standards) presents a logical one, while 0V presents logical zero. In real systems this bounding is fuzzy and brings some tolerance, simplifying, i.e. communication from 3.3 V output to 5 V input, without a need of the conversion (note, the reverse conversion is usually not so straightforward, as 3.3 V inputs driven by 5V output may burn easily). A sample of the sensor providing binary data is a button (On/Off).

SPI

One of the most popular interfaces to connect the sensor is SPI (Serial Peripheral Interface). It is a synchronous serial interface and protocol that can transmit data with speed up to 20 Mbps. SPI is used to communicate microcontrollers with one or more peripheral devices over short distances – usually internally in the device. In SPI connection there is always one master device, in most cases the microcontroller (μC) that controls the transmission, and one or more slave devices – peripherals. To communicate SPI uses three lines common to all of the connected devices, and one enabling line for every slave element.

Table 9: SPI Lines
Line Description Direction
MISO Master In Slave Out peripheral → μC
MOSI Master Out Slave In μC → peripheral
SCK Serial Clock μC → peripheral
SS Slave Select μC → peripheral

MISO is intended to send bits from slave to master, MOSI transmits data from master to slave. SCK line is used for sending clock pulses which synchronize data transmission. The clock signal is always generated by the master device. Every SPI compatible device has the SS (Slave Select) input that enables communication in this specific device. Master is responsible to generate this enable signal – separately for every slave in the system.

 SPI connections
Figure 114: Sample SPI connection.

SPI is used in many electronic elements like analogue to digital converters (ADC), real-time clocks (RTC), EEPROMs, LCD displays, communication interfaces (e.g. Ethernet, WiFi) and many others. Due to different hardware implementations, there are four modes of operation of the SPI protocol. The mode used in master must fit the mode that is implemented in the slave device.

Table 10: SPI Modes
Mode Clock polarity Clock phase Idle state Active state Output edge Data capture
mode 0 0 0 0 1 falling rising
mode 1 0 1 0 1 rising falling
mode 2 1 0 1 0 rising falling
mode 3 1 1 1 0 falling rising

It results in different timings of the clock signal concerning the data sent. Clock polarity = 0 means that the idle state of the SCK is 0, so every data bit is synchronised with the pulse of logic 1. Clock polarity = 1 reverses these states. Output edge (rising/falling) says at which edge of active SCK signal sender puts a bit on the data line. Data capture edge says at what edge of SCK signal data should be captured by the receiver.

TWI (I2C)

TWI (Two Wire Interface) is one of the most popular communication protocol used in embedded systems. It has been designed by Philips as I2C (Inter-Integrated Circuit) for using in the audio-video appliances controlled by the microprocessor. There are many chips that can be connected to the processor with this interface, including:

  • EEPROM memory chips,
  • RAM memory chips,
  • AD/DA converters,
  • real-time clocks,
  • sensors (temperature, pressure, gas, air pollution),
  • port extenders,
  • displays,
  • specialised AV circuits.

TWI, as the name says, uses two wires for communication. One is the data line (SDA); the second is the clock line (SCL). Both lines are common to all circuits connected to the one TWI bus. The method of the communication of TWI is the master-slave synchronous serial transmission. It means that data is sent bit after bit synchronised with the clock signal. SCL line is always controlled by the master unit (usually the processor), the signal on the SDA line is generated by the master or one of the slaves – depending on the direction of communication. The frequency rate of the communication is up to 100 kHz for most of the chips, for some can be higher – up to 400 kHz. The new implementation allows even higher frequency rate is reaching 5 MHz. At the output side of units, the lines have the open-collector or open-drain circuit. It means that there are external pull-up resistors needed to ensure proper operation of the TWI bus. Value of these resistors depends on the number of connected elements, speed of transmission and the power supply voltage and can be calculated with the formulas presented in Texas Instrument Application Report [103]. Usually, it is assumed between 1 kΩ and 4.7 kΩ.

 TWI bus connection
Figure 115: Sample TWI connection.

The data is sent using frames of bytes. Every frame begins with the sequence of signals that is called the start condition. This sequence is detected by slaves and causes them to collect the next eight bits that form the address byte – unique for every circuit on the bus. If one of the slaves recognises its address remains active until the end of the communication frame, others become inactive. To inform the master that some unit has been appropriately addressed slave responses with the acknowledge bit – it generates one bit of low level on the SDA line (the master generates clock pulse). After sending the proper address, data bytes are sent. The direction of the data bytes is controlled by the last bit of the address, for 0 data is transmitted by the master (Write), for 1 data is sent by the slave (Read). The receiving unit must acknowledge every full byte (eight bits). There is no limitation on the number of data bytes in the frame, for example, samples from the AD converter can be read byte continuously after byte. At the end of the frame, another special sequence is sent by the master – stop condition. It is also possible to generate another start condition without the stop condition. It is called a repeated start condition.

 TWI frame
Figure 116: TWI frame.

Address byte activates one chip on the bus only, so every unit must have a unique physical address. This byte usually consists of three elements: 4-bit field fixed by the producer, 3-bit field that can be set by connecting three pins of the chip to 0 (ground) or 1 (positive supply line), 1-bit field for setting the direction of communication (R/#W). Some elements (e.g. EEPROM memory chips) uses the 3-bit field for internal addressing so there can be only one such circuit connected to one bus. There are no special rules for the data bytes. First data byte sent by the master can be used for configuration of the slave chip. In memory units, it is used for setting the internal address of the memory for writing or reading, in multi-channel AD converters to choose the analogue input. The detailed information on the meaning of every bit of the transmission is present in the documentation of the specific integrated circuit. The I2C standard also defines the multi-master mode, but in most of the small projects, there is one master device only.

1-Wire

1-Wire is a master-slave communication bus system designed formerly by Dallas Semiconductor Corp[104] ensuring low data transmission speed, signalling and can be powered directly by data line signals. The 1-Wire concept is similar to I²C transmission standard, but can transmit data in longer distances then I²C but with lower speed. The implementation area is very wide and typically 1-Wire protocol is used to share data between small, inexpensive devices such as a digital thermometer, humidity or pressure sensors or actuator systems. A network chain of 1-Wire devices consists of one master device and many slave devices. Such a chain is called a MicroLAN. 1-Wire devices may be a part of the circuit board within a product, could be a single component device such as temperature probe, or may be attached to a remote device for monitoring purposes. Typical data acquisition and laboratory networks use CAT-5 cables to connect 1-Wire devices together, can be mounted in a socket of small PCB boards, attached to the device which must be monitored. In such implementations, the RJ11 connectors (telephones 6P2C/6P4C modular plugs) are very popular. Each 1-Wire device must contain logic unit to operate on the bus. The 1-Wire products include temperature, voltage, current sensors, loggers, timers, battery monitors, memory and many more. To connect them to a PC the special bus converter is needed. The most popular PC/1-Wire converters use USB, RS-232 serial, and parallel port interfaces allowing connect the MicroLAN to the host PC. 1-Wire devices can also be connected directly to the microcontroller boards.

1-Wire Protocol Description

Within the MicroLAN, there is always one master device, which may be a PC or a microcontroller unit. The master always initiates activity on the bus to avoid collisions on the network chain. If a collision occurs, the master device retries the communication. In the 1-Wire network, many devices can share the same bus line. To identify devices in the MicroLAN, each connected device has a unique 64-bit ID number. The least significant byte of the ID number defines the type of the device (temperature, voltage etc. sensors). The most significant byte represents a standard 8-bit CRC. The 1-Wire protocol description contains several broadcast commands and commands used to address the selected device. The master sends a selection command, then the address of a slave selected device. This way, the next command is executed only by the addressed device. The 1-Wire bus implements enumeration procedure which allows the master to get information about ID numbers of all connected slave devices to the MicroLAN network. Device address includes the device type, and a CRC allows to identify what type of slaves are currently connected to the network chain for inventory purposes. The 64-bit address space is searched as a binary tree. It allows to find up to 75 devices per second.

The physical implementation of the 1-Wire network is based on an open drain master device connected to one or more open drain slaves. One single pull-up resistor for all devices pull the bus up to 3/5 V and can be used to power the slave devices. 1-Wire communication starts when a master or slave sets the bus to low voltage (connects the pull-up resistor to ground through its output MOSFET). Typical data speed of the 1-Wire interface is about 16.3 kbit/s.

1-Wire protocol allows for bursting the communication speed up by 10 factor. In this case, the master starts a transmission with a reset pulse pulling down the data line to 0 volts for at least 480 µs. It resets all slave devices in the network chain bus. Then, any slave device shows that it exists generating the “presence” pulse. It holds the data line low for at least 60 µs after the master releases the bus. To send a “1”, the bus master sends a 1–15 µs low pulse. To send a “0”, the master sends a 60 µs low pulse. The negative edge of the pulse is used to start a slave's monostable multivibrator. The slave's multivibrator clocks to read the data bus about 30 µs after the falling edge. The slave's multivibrator has analogue tolerances that affect its timing accuracy, for the “0” pulses are 60 µs long, and “1” pulses are limited to max 15 µs. When the designed solution doesn't contain a dedicated 1-Wire interface peripheral, a UART can be used as a 1-Wire master. Dallas also offers the Serial or USB “bridge” chips, very useful when the distance between devices is long (greater than 100 m). For longer, up to 300 m buses, the simple twisted pair telephone cable can be used. It will require adjustment of pull-up resistances from 5 kΩ to 1 kΩ. The basic sequence is a reset pulse followed by an 8-bit command, and after it, data can be sent/received in groups of 8-bits. In the case of transmission errors, the weak data protection 8-bit CRC checking procedure can be used.

To find the devices, the enumeration broadcast command must be sent by a master. The slave device response with all ID bits to the master and at the end it returns a 0.

Figure 117: 1-Wire reset timings.
Figure 118: 1-Wire read timings.
Figure 119: 1-Wire write timings.

USB to 1-Wire Master

The DS9490B is a USB bridge and holder for a single F5-size iButton. The DS9490R is a USB bridge with 1-Wire RJ11 interface to accommodate 1-Wire receptacles and networks.

Figure 120: DS9490R USB Bridge.

The bridge is based on the DS2490 chip developed by Dallas company, which allows to interconnect USB interface with 1-Wire bus. This required programming and electrical conversion between two different protocols in bidirectional way. The electrical wiring are present on Figure 9.

Figure 121: DS9490R USB schematic.

The appropriate 1-Wire cable pinout uses RJ11 telephone connectors.

Figure 122: DS9490 1-Wire RJ11 SOCKET pinout.

1-Wire Products

The list of Dallas/Maxim integrated 1-Wire devices contains a wide range of industrial implementations. The 1-Wire sensors and switches devices are very popular in the developer's community due to ease implementation. 1-Wire protocol can be fast implemented into the current IoT boards; most of the manufacturers share the software libraries allowing developers to include them in their projects in C, C++, assembly languages. The 1-Wire sensors (temperature, humidity, pressure, etc.) are factory calibrated and reading the physical measurements follows the International System of Units (SI). 1-Wire products can be grouped as follows:

  • secure authenticators,
  • memory EPROM, EEPROM ROM,
  • temperature sensors and temperature switches,
  • data loggers,
  • 1-Wire interface products,
  • battery monitors, protectors, and selectors,
  • battery ID and authentication,
  • timekeeping and real-time clocks.

Arduino Overview

No doubt, Arduino became the most widespread SoC, particularly among enthusiasts, educators, amateurs, and hobbyists, driving de-facto the embedded systems market for years.

Using cheap Atmel AVR microcontrollers, delivered along with development board and peripherals of almost any kind, including sensors and actuators, where you do not need to develop your PCB nor solder to obtain the fully functional device, all that triggered a new era where almost anyone can afford to have a development set and start playing the way only professionals used to do. Moreover, Arduino was not only the hardware but also the programming idea, delivering a simple development environment that is easy to use for beginners. Perhaps the most important impact of the Arduino on daily use was to spread the idea of taking automation control from the industry and bringing it on a massive scale to regular life, homes, cars, and toys to automate daily life.

The beginnings of the Arduino are dated to the year 2003 in Italy. Their most popular development board was delivered to the market in the fall of 2010. While AVRs microcontrollers are considered to be embedded systems more than IoT, and most of the early Arduino boards didn't offer any network interface, even then, it is essential to understand the idea of how to work with SoCs, so we start our guide here. However, many extension boards are present, suitable for the standard development boards (so-called shields) that offer wired and wireless networking for Arduino. Some of the Arduino development boards nowadays do integrate networking SoC into one board, i.e. Arduino Yun. Also, their clones, mostly made by Chinese manufacturers, evolved into more sophisticated products, integrating, i.e. Arduino Mega 2560 and ESP8266 SoC into one development board.

The following chapters present the Arduino hardware overview, peripherals and programming as universal basics for IoT systems development using advanced processors like ESP:

Overview of the Hardware Device Used

What is Arduino and Why to Use It?

Arduino is an open-source platform based on easy-to-use hardware and software [105]. The Arduino project was started at the Ivrea Interaction Design Institute in Italy. Initially, the board aimed at students without a background in electronics and programming, but now boards are suitable for different IoT applications, wearable, embedded environments and other.

The Arduino board works by reacting on inputs that are received from various sensors and, after executing a set of instructions, an output is generated to respond to the environment. Input can be received by pressing a button, hearing the noise, perceiving an image of the situation using a camera and many other. The output actions on the environment are done using output sensors like actuator, blinking LED, audio device and other. The set of instructions are created using the Arduino programming language that is based on an open-source programming framework called Wiring and the Arduino Software (IDE) that is based on Processing.

Arduino microcontrollers can be used both in research and everyday applications. It is easy to use for people with different backgrounds, from students to experts. The Arduino Forum [106] is the place where users of Arduino can share their knowledge and get help and new ideas for developing their project.

The Most Common Arduino Boards

Arduino boards can be divided into six sections depending on their specifications – entry level, enhanced features, Internet of things, education, wearable, and 3D printing boards.

The most common boards of Arduino are Uno, Leonardo, Micro, Nano (entry level), Mega, Pro Mini (enhanced features). Each of the board has different specifications and therefore, can have different applications.

 title
Figure 123: The most common Arduino boards.

Digital Input/Output Pins

Digital input/output (I/O) pins are contacts on the Arduino board that can receive or transmit a digital signal. The status of the pin can be set either to 0 that represents LOW signal or to 1 – HIGH signal. The maximum current of the pin output is 40 mA.

Table 11: The Comparison of Arduino Boards by the Digital I/O Pin Number
Uno Leonardo Micro Mega Nano Pro Mini
Digital I/O 14 20 20 54 22 14

Pulse Width Modulation

Pulse Width Modulation (PWM) is a function of a pin to generate a square wave signal, with a variable length of the HIGH level of the output signal. The PWM is used for digital pins to simulate the analogue output.

Table 12: The Comparison of Arduino Boards by the Digital PWM Pin Number
Uno Leonardo Micro Mega Nano Pro Mini
PWM 6 7 7 12 6 6

Analog Pins

Analog pins convert the analogue input value to a 10-bit number, using Analog Digital Converter (ADC). This function maps the input voltage between 0 and the reference voltage to numbers between 0 and 1023.

By default, the reference voltage is set to a microcontroller operating voltage. Usually, it is 5 V or 3.3 V. Also, other internal or external reference sources can be used, for example, AREF pin.

Table 13: The Comparison of Arduino Boards by the Analog Pin Number
Uno Leonardo Micro Mega Nano Pro Mini
Analog pins 6 12 12 16 8 6

Power and Other Pins

Power pins on the Arduino board connect the power source to the microcontroller and/or voltage regulators. They can also be used as a power source to the external components and devices.

The VIN pin is used to connect the external power source to the internal regulator, to provide the regulated 5 V output. The input voltage of the board must be within the specific range, mostly between 7 V and 12 V.

The 5V pin is used to supply a microcontroller with the regulated 5 V from the external source or is used as a power source for the external components in the case when the board is already powered using the USB interface or the VIN pin.

The 3V3 pin provides the regulated 3.3 V output for the board components and external devices. The GND (ground pin) is where the negative terminal of the power supply is applied.

The Reset pin and the reset button are used to reset the Arduino board and the program. Resetting using the reset pin is done by connecting it to the GND pin.

Memory

There are three different types of memory on the Arduino board: flash memory, SRAM and EEPROM.

The flash memory stores the Arduino code, and it is a non-volatile type of memory. That means the information in the memory is not deleted when the power is turned off.

The SRAM (static random access memory) is used for storing values of variables when the program of Arduino is running. This is the volatile memory that keeps information only until the power is turned off, or the board is reset.

The EEPROM (electrically erasable programmable read-only memory) is a non-volatile type of memory that can be used as the long-term memory storage.

Table 14: The Comparison of Arduino Boards by Memory Size
Uno Leonardo Micro Mega Nano Pro Mini
Flash (kB) 32 32 32 256 32 32
SRAM (kB) 2 2 2.5 8 2 2
EEPROM (kB) 1 1 1 4 1 1

Interface

Communication interfaces for Arduino are used to send and receive information to and from other external devices. Standard interfaces for Arduino are USB, UART, I2C (two wire interface), SPI, Ethernet and WiFi.


Table 15: The Comparison of Arduino Boards by Interface Available
Uno Leonardo Micro Due Nano Pro Mini
USB 1 USB B 1 Micro 1 Micro 1 USB B 1 Mini
UART 1 1 1 4 1 1
Wire(I2c) 1 1 1 1 1 1
SPI 1 1 1 1 1 1

Size of the Board

Arduino microcontrollers have different dimensions of the board, depending on the components that are located on the board.

Table 16: The Comparison of Arduino Boards by the Size of the Board
Uno Leonardo Micro Mega Nano Pro Mini
Size (mm) 68.6 × 53.4 68.6 × 53.3 48 × 18 101.52 × 53.3 18 × 45 18 × 33

Arduino Shields

Arduino shields are the extension boards that can be plugged on top of the Arduino board extending its capabilities. The shields can give additional functionality to the Arduino board. There are multiple categories of the Arduino shields [107]prototyping, improving connectivity, displays and cameras, sound and motor driver shields.

Prototyping shields – are shields that do not give Arduino the additional functionality, but help with the wiring. Some example prototyping shields are ProtoShield, ProtoScrew Shield, Go-Between Shield, LiPower Shield, Danger Shield, Joystick Shield and microSD Shield.

 title
Figure 124: Prototype shield.

Connectivity shields – are shields that can add new functionalities to the Arduino board like Ethernet, WiFi, Wireless, GPS, etc. Example shields are Arduino Ethernet Shield, WiFly Shield, Arduino Wi-Fi Shield, Electric Imp Shield, XBee Shield, Cellular Shield SM5100B and GPS Shield.

 title
Figure 125: Arduino wifi shield MKR WIFI 1010.

Displays and camera shields – can provide Arduino with an LCD screen or add a camera. Example shields are Color LCD Shield, EL Escudo and CMUcam.

 title
Figure 126: SparkFun Color LCD Shield.

Sound shields – give the functionality to Arduino to play MP3 files, add speakers, listen to audio and sort it into different frequencies, etc. Example shields are MP3 Player Shield, Music Instrument Shield, Spectrum Shield and VoiceBox Shield.

 title
Figure 127: SparkFun MP3 Player Shield.

Motor driver shields – allow Arduino to control DC motors, Servo motors, Stepper motors. Examples are Ardumoto Motor Driver Shield, Monster Moto Shield and PWM Shield.

 title
Figure 128: Adafruit Servo shield.

Sensors and Sensing

A sensor is an element which can turn a physical outer stimulus into an output signal which then can be used for further analysis, the management or decision making. People also use sensors like eyes, ears and skin for gaining information about the outer world and act accordingly to their aims and needs. Sensors can be divided into multiple categories by the parameter that is perceived from the environment.

 title
Figure 129: Environment sensing data flow.

Usually, every natural phenomenon – temperature, weight, speed, etc. – needs specially customised sensors which can change every phenomenon into electronic signals that could be used by microprocessors or other devices. Sensors can be divided into many groups according to the physical nature of their operations – touch, light, an electrical characteristic, proximity and distance, angle, environment and other sensors.

Touch Sensors

Button

A pushbutton is an electromechanical sensor that connects or disconnects two points in a circuit when the force is applied. Button output discrete value is either HIGH or LOW.

 title
Figure 130: Pushbutton.

A microswitch, also called a miniature snap-action switch, is an electromechanical sensor that requires a very little physical force and uses tipping-point mechanism. Microswitch has three pins, two of which are connected by default. When the force is applied, the first connection breaks and one of the pins is connected to the third pin.

 title
Figure 131: Microswitch.

The most common use of a pushbutton is as an input device. Both force solutions can be used as simple object detectors, or as end switches in the industrial devices.

 title
Figure 132: Schematics of Arduino Uno and a push button.

An example code:

int buttonPin = 2; //Initialization of a push button pin number
int buttonState = 0; //A variable for reading the push button status
 
void setup() {
  Serial.begin(9600);  //Begin serial communication
  pinMode(buttonPin, INPUT); //Initialize the push button pin as an input
}
 
void loop() {
  //Read the state of the push button value
  buttonState = !digitalRead(buttonPin);
  //Check if the push button is pressed. If it is, the buttonState is HIGH
  if (buttonState == HIGH) { 
    //Print out text in the console
    Serial.println("The button state is HIGH - it is pressed."); 
  } else {
    Serial.println("The button state is LOW - it is not pressed.");
  }
  delay(10); //Delay in between reads for stability
}
Force Sensor

A force sensor predictably changes resistance, depending on the applied force to its surface. Force-sensing resistors are manufactured in different shapes and sizes, and they can measure not only direct force but also the tension, compression, torsion and other types of mechanical forces. The voltage is measured by applying and measuring constant voltage to the sensor.

Force sensors are used as control buttons or to determine weight.

 title
Figure 133: Force sensitive resistor (FSR).
title
Figure 134: The voltage is measured by applying and measuring constant voltage to the sensor.

An example code:

//Force Sensitive Resistor (FSR) is connected to the analog 0 pin
int fsrPin = A0; 
//The analog reading from the FSR resistor divider
int fsrReading;      
 
void setup(void) {
  //Begin serial communication
  Serial.begin(9600);   
  //Initialize the FSR analog pin as an input
  pinMode(fsrPin, INPUT); 
}
 
void loop(void) {
  //Read the resistance value of the FSR
  fsrReading = analogRead(fsrPin); 
  //Print 
  Serial.print("Analog reading = "); 
  Serial.println(fsrReading);
  delay(10);
}
Capacitive Sensor

Capacitive sensors are a range of sensors that use capacitance to measure changes in the surrounding environment. A capacitive sensor consists of a capacitor that is charged with a certain amount of current until the threshold voltage. A human finger, liquids or other conductive or dielectric materials that touch the sensor, can influence a charge time and a voltage level in the sensor. Measuring charge time and a voltage level gives information about changes in the environment.

Capacitive sensors are used as input devices and can measure proximity, humidity, fluid level and other physical parameters or serve as an input for electronic device control.

 title
Figure 135: Touch button module.
 title
Figure 136: Arduino and capacitive sensor schematics.
//Capacitive sensor is connected to the digital 2 pin
int touchPin = 2; 
 
//The digital reading value from the sensor
boolean touchReading = LOW; 
//The variable that stores the previous state value
boolean lastState = LOW; 
 
void setup() {
  //Begin serial communication
  Serial.begin(9600);  
  //Initialize the capacitive sensor analog pin as an input
  pinMode(touchPin, INPUT);  
}
 
void loop() {
  //Read the digital value of the capacitive sensor
  touchReading = digitalRead(touchPin); 
  //If the new touch has appeared
  if (currentState == HIGH && lastState == LOW){ 
    Serial.println("Sensor is pressed");
    delay(10); //short delay
  }
  //Save previous state to see relative changes
  lastState = currentState; 
}

Light Sensors

Photoresistor

A photoresistor is a sensor that perceives light waves from the environment. The resistance of the photoresistor is changing depending on the intensity of light. The higher is the intensity of the light; the lower is the resistance of the sensor. A light level is determined by applying a constant voltage sensor and measuring it. Photodiodes, compared to photoresistors, are slower and more influenced by temperature; thus, they are more imprecise.

Photoresistors are often used in the energy effective street lightning.

 title
Figure 137: A photoresistor symbol.
 title
Figure 138: A photoresistor.
 title
Figure 139: Arduino and photoresistor sensor schematics.

An example code:

//Define an analog A0 pin for photoresistor
int photoresistorPin = A0; 
//The analog reading from the photoresistor 
int photoresistorReading;  
 
void setup()
{
    //Begin serial communication
    Serial.begin(9600);  
    //Initialize the analog pin of a photoresistor as an input
    pinMode(photoresistorPin, INPUT); 
}
 
void loop()
{
    //Read the value of the photoresistor
    photoresistorReading = analogRead(photoresistorPin); 
    //Print out value of the photoresistor reading to the serial monitor
    Serial.println(photoresistorReading); 
    delay(10); //Short delay
}
Photodiode

A photodiode is a sensor that converts the light energy into electrical current. A current in the sensor is generated by exposing a p-n junction of a semiconductor to the light. Information about the light intensity can be determined by measuring a voltage level. Photodiodes are reacting to the changes in the light intensity very quickly. Solar cells are just large photodiodes.

Photodiodes are used as precise light level sensors, receivers for remote control, electrical isolators and proximity detectors.

 title
Figure 140: A photodiode symbol.
 title
Figure 141: A photodiode.
 title
Figure 142: Arduino and photodiode sensor schematics.

An example code:

//Define an analog A0 pin for photodiode
int photodiodePin = A0;  
//The analog reading from the photodiode
int photodiodeReading;  
 
void setup()
{
    //Begin serial communication
    Serial.begin(9600);  
    //Initialize the analog pin of a photodiode as an input
    pinMode(photodiodePin, INPUT); 
}
 
void loop()
{
    //Read the value of the photodiode
    photodiodeReading = analogRead(photodiodePin); 
    //Print out the value of the photodiode reading to the serial monitor
    Serial.println(photodiodeReading); 
    delay(10); //Short delay
}
Phototransistor

A phototransistor is a light controlled electrical switch. In the exposed Base pin received light level, changes the amount of current, that can pass between two phototransistor pins – a collector and an emitter. A phototransistor is slower than the photodiode, but it can conduct more current.

Phototransistors are used as the optical switches, proximity sensors and electrical isolators.

 title
Figure 143: A phototransistor symbol.
 title
Figure 144: An phototransistor.
 title
Figure 145: Arduino and phototransistor schematics.

An example code:

//Define an analog A1 pin for phototransistor
int phototransistorPin = A1;  
//The analog reading from the phototransistor
int phototransistorReading;  
 
void setup()
{
    //Begin serial communication
    Serial.begin(9600);  
    //Initialize the analog pin of a phototransistor as an input
    pinMode(phototransistorPin, INPUT); 
}
 
void loop()
{
    //Read the value of the phototransistor
    phototransistorReading = analogRead(phototransistorPin); 
    //Print out the value of the phototransistor reading to the serial monitor
    Serial.println(phototransistorReading); 
    delay(10); //short delay
}

Electrical Characteristic Sensors

Electrical characteristic sensors are used to determine whether the circuit of the device is working properly. When the voltage and current sensors are used concurrently, the consumed power of the device can be determined.

Voltage Sensor

A voltage sensor is a device or circuit for voltage measurement. A simple DC (direct current) voltage sensor consists of a voltage divider circuit with the optional amplifier for very small voltage occasions. For measuring the AC (alternating current), a transformer is added to a lower voltage; then it is connected to the rectifier to rectify AC to DC, and finally, an optoelectrical isolator is added for measuring circuit safety.

A voltage sensor can measure electrical load and detect a power failure. Examples of IoT applications are monitoring of appliance, line power, power coupling, power supply and sump pump.

 title
Figure 146: Voltage sensor module 0–25 V.
 title
Figure 147: Arduino and voltage sensor schematics.

The example code:

//Define an analog A1 pin for voltage sensor
int voltagePin = A1; 
//The analog reading from the voltage sensor
int voltageReading;  
 
float vout = 0.0;
float vin = 0.0;
float R1 = 30000.0; //  30 kΩ resistor 
float R2 = 7500.0; //  7.5 kΩ resistor
 
void setup()
{
    //Begin serial communication
    Serial.begin(9600);  
    //Initialize the analog pin of a voltage sensor as an input
    pinMode(voltagePin, INPUT); 
}
 
void loop()
{
    //Read the value of the voltage sensor
    voltageReading = analogRead(voltagePin); 
    vout = (voltageReading * 5.0) / 1024.0;
    vin = vout / (R2/(R1+R2));
 
    Serial.print("Voltage is: ");
    //Print out the value of the voltage to the serial monitor
    Serial.println(vin); 
    delay(10); //Short delay
}
Current Sensor

A current sensor is a device or a circuit for current measurement. A simple DC sensor consists of a high power resistor with low resistance. The current is obtained by measuring the voltage on the resistor and applying formula proportional to the voltage. Other non-invasive measurement methods involve hall effect sensors for DC and AC and inductive coils for AC. Current sensors are used to determine the power consumption, to detect whether the device is turned on, short circuits.

 title
Figure 148: Analog current meter module 0–50 A.
 title
Figure 149: Arduino and current sensor module schematics.

The example code:

//Define an analog A0 pin for current sensor
const int currentPin = A0; 
//Scale factor of the sensor use 100 for 20 A Module and 66 for 30 A Module
int mVperAmp = 185; 
int currentReading;
int ACSoffset = 2500; 
double Voltage;
double Current;
 
void setup(){ 
 Serial.begin(9600);
}
 
void loop(){
 
 currentReading = analogRead(currentPin);
 Voltage = (currentReading / 1024.0) * 5000; //Gets you mV
 Current = ((Voltage - ACSoffset) / mVperAmp); //Calculating current value
 
 Serial.print("Raw Value = " ); //Shows pre-scaled value 
 Serial.print(currentReading); 
 Serial.print("\t Current = "); //Shows the voltage measured
 //The '3' after current allows to display 3 digits after decimal point
 Serial.println(Current,3); 
 delay(1000); //Short delay

Proximity and Distance Sensors

Optocoupler

An optocoupler is a device that combines light emitting and receiving devices. Mostly it is a combination of the infrared light-emitting diode (LED) and a phototransistor. Other optical semiconductors can be a photodiode and a photoresistor. There are two main types of optocouplers:

  • an optocoupler of a closed pair configuration is enclosed in the dark resin and is used to transfer signals using light, ensuring electrical isolation between two circuits;
  • a slotted optocoupler has a space between the light source and the sensor, light can be obstructed and thus can influence the sensor signal. It can be used to detect objects, rotation speed, vibrations or serve as a bounce-free switch;
  • a reflective pair configuration the light signal is perceived as a reflection from the object surface. This configuration is used for proximity detection, surface colour detection and tachometer.
 title
Figure 150: An optocoupler symbol.
 title
Figure 151: ELITR9909 reflective optocoupler sensor.
 title
Figure 152: Arduino Uno and optocoupler schematics.

An example code:

int optoPin = A0; //Initialize an analog A0 pin for optocoupler
int optoReading; //The analog value reading from the optocoupler
 
int objecttreshold = 1000; //Object threshold definition
int whitetreshold = 150; //White colour threshold definition
 
void setup () 
{
  //Begin serial communication
  Serial.begin(9600); 
  //Initialize the analog pin of the optocoupler as an input
  pinMode(optoPin, INPUT); 
}
 
void loop () 
{
  optoReading = analogRead(optoPin); //Read the value of the optocoupler
  Serial.print ("The reading of the optocoupler sensor is: ");
  Serial.println(optoReading);
 
  //When the reading value is lower than the object threshold
  if (optoReading < objecttreshold) { 
    Serial.println ("There is an object in front of the sensor!");
    //When the reading value is lower than the white colour threshold
    if (optoReading < white threshold) { 
      Serial.println ("Object is in white colour!");
    } else { //When the reading value is higher than the white colout threshold
      Serial.println ("Object is in dark colour!");
    }
  }
  else { //When the reading value is higher than the object thershold
    Serial.println ("There is no object in front of the sensor!");
  }
  delay(500); //Short delay
}
Infrared Sensor

Infrared (IR) proximity sensor is used to detect objects and to measure the distance to them, without any physical contact. IR sensor consists of an infrared emitter, a receiving sensor or array of sensors and a signal processing logic. The output of a sensor differs depending on the type – simple proximity detection sensor outputs HIGH or LOW level when an object is in its sensing range, but sensors which can measure distance outputs an analogue signal or use some communication protocol, like I2C to send sensor measuring results. IR sensors are used in robotics to detect obstacles starting from few millimetres to several meters and in mobile phones to help detect accidental button touching.

 title
Figure 153: Distance Sensor GP2Y0A21YK0F.
 title
Figure 154: Arduino and IR proximity sensor circuit.

An example code:

int irPin = A0;  //Define an analog A0 pin for IR sensor
int irReading;  //The analog reading from the IR sensor
 
void setup()
{
    //Begin serial communication
    Serial.begin(9600);  
    //Initialize the analog pin of a IR sensor as an input
    pinMode(irPin, INPUT); 
}
 
void loop()
{
    //Read the value of the IR sensor
    irReading = analogRead(irPin); 
    //Print out the value of the IR sensor reading to the serial monitor
    Serial.println(irReading); 
    delay(10); //Short delay
}
Ultrasound Sensor

Ultrasound (ultrasonic) sensor measures the distance to objects by emitting ultrasound and measuring its returning time. The sensor consists of an ultrasonic emitter and receiver; sometimes, they are combined in a single device for emitting and receiving. Ultrasonic sensors can measure greater distances and cost less than infrared sensors, but are more imprecise and interfere which each other measurement if more than one is used. Simple sensors have trigger pin and echo pin, when the trigger pin is set high for the small amount of time ultrasound is emitted and on echo pin, response time is measured. Ultrasonic sensors are used in car parking sensors and robots for proximity detection.

 title
Figure 155: Ultrasonic proximity sensor HC-SR04.

Examples of IoT applications are robotic obstacle detection and room layout scanning.

 title
Figure 156: Arduino and ultrasound proximity sensor circuit.

An example code:

int trigPin = 2;  //Define a trigger pin D2
int echoPin = 4;  //Define an echo pin D4
 
void setup()
{
    Serial.begin(9600); //Begin serial communication
    pinMode(trigPin, OUTPUT); //Set the trigPin as an Output
    pinMode(echoPin, INPUT); //Set the echoPin as an Input
}
 
void loop()
{
    digitalWrite(trigPin, LOW);  //Clear the trigPin
    delayMicroseconds(2);
 
    //Set the trigPin on HIGH state for 10 μs
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
 
    //Read the echoPin, return the sound wave travel time in microseconds
    duration = pulseIn(echoPin, HIGH); 
    //Calculating the distance 
    distance= duration*0.034/2; 
 
    //Printing the distance on the Serial Monitor
    Serial.print("Distance: ");  
    Serial.println(distance);
}
Motion Detector

The motion detector is a sensor that detects moving objects, most people. Motion detectors use different technologies, like passive infrared sensors, microwaves and Doppler effect, video cameras and previously mentioned ultrasonic and IR sensors. Passive IR sensors are the simplest motion detectors that sense people trough detecting IR radiation that is emitted through the skin. When the motion is detected, the output of a motion sensor is a digital HIGH/LOW signal.

Motion sensors are used for security purposes, automated light and door systems. As an example in IoT, the PIR motion sensor can be used to detect motion in security systems a house or any building.

 title
Figure 157: PIR motion sensor.
 title
Figure 158: Arduino and PIR motion sensor circuit.

An example code:

//Passive Infrared (PIR) sensor output is connected to the digital 2 pin
int pirPin = 2; 
//The digital reading from the PIR output
int pirReading;      
 
void setup(void) {
  //Begin serial communication
  Serial.begin(9600);   
  //Initialize the PIR digital pin as an input
  pinMode(pirPin, INPUT); 
}
 
void loop(void) {
  //Read the digital value of the PIR motion sensor
  pirReading = digitalRead(pirPin); 
  //Print out
  Serial.print("Digital reading = "); 
  Serial.println(pirReading);
 
  if(pirReading == HIGH) {  //Motion was detected
    Serial.println("Motion Detected");
  }
 
  delay(10);
}

Angle Sensors

Potentiometer

A potentiometer is a type of resistor, the resistance of which can be adjusted using a mechanical lever. The device consists of three terminals. The resistor between the first and the third terminal has fixed value, but the second terminal is connected to the lever. Whenever the lever is turned, a slider of the resistor is moved, it changes the resistance between the second terminal and side terminals. Variable resistance causes the change of the voltage variable, and it can be measured to determine the position of the lever. Thus, potentiometer output is an analogue value.

Potentiometers are commonly used as a control level, for example, a volume level for the sound and joystick position. They can also be used for angle measurement in feedback loops with motors, for example, in servo motors.

 title
Figure 159: A symbol of potentiometer.
 title
Figure 160: A potentiometer.
 title
Figure 161: Arduino and potentiometer circuit.

An example code:

//Potentiometer sensor output is connected to the analog A0 pin
int potentioPin = A0; 
//The analog reading from the potentiometer output
int potentioReading;      
 
void setup(void) {
  //Begin serial communication
  Serial.begin(9600);   
  //Initialize the potentiometer analog pin as an input
  pinMode(potentioPin, INPUT); 
}
 
void loop(void) {
  //Read the analog value of the potentiometer sensor
  potentioReading = analogRead(potentioPin); 
  Serial.print("Potentiometer reading = "); //Print out
  Serial.println(potentioReading);
  delay(10);
}
The Inertial Measurement Unit (IMU)

An IMU is an electronic device, that consist of accelerometer, gyroscope and sometimes also a magnetometer. Combination of these sensors returns the orientation of the object in 3D space.

A gyroscope is a sensor that measures the angular velocity. The sensor is made of the microelectromechanical system (MEMS) technology and is integrated into the chip. The output of the sensor can be either analogue or digital value of information, using I2C or SPI interface. Gyroscope microchips can vary in the number of axes they can measure. The available number of the axis is 1, 2 or 3 axes in the gyroscope. For gyroscopes with 1 or 2 axes, it is essential to determine which axis the gyroscope measures and to choose a device according to the project needs. A gyroscope is commonly used together with an accelerometer, to determine the orientation, position and velocity of the device precisely. Gyroscope sensors are used in aviation, navigation and motion control.

A magnetometer is the sensor, that can measure the orientation of the device to the magnetic field of the Earth. A magnetometer is used in outdoor navigation for mobile devices, robots, quadcopters.

An accelerometer measures the acceleration of the object. The sensor uses a microelectromechanical system (MEMS) technology, where capacitive plates are attached to springs. When acceleration force is applied to the plates, the capacitance is changed; thus, it can be measured. Accelerometers can have 1 to 3 axis. On 3-axis, the accelerometer can detect orientation, shake, tap, double tap, fall, tilt, motion, positioning, shock or vibration of the device. Outputs of the sensor are usually digital interfaces like I2C or SPI. For precise measurement of the object movement and orientation in space, the accelerometer is often used together with a gyroscope. Accelerometers are used for measuring vibrations of cars, industrial devices, buildings and to detect volcanic activity. In IoT applications, it can be used as well for accurate motion detection for medical and home appliances, portable navigation devices, augmented reality, smartphones and tablets.

 title
Figure 162: IMU BNO055 module.
 title
Figure 163: Arduino Uno and IMU BNO055 module schematics.

The example code:

//Library for I2C communication
#include <Wire.h>
//Downloaded from https://github.com/adafruit/Adafruit_Sensor
#include <Adafruit_Sensor.h>
//Downloaded from https://github.com/adafruit/Adafruit_BNO055 
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>
Adafruit_BNO055 bno = Adafruit_BNO055(55);
void setup(void)
{
bno.setExtCrystalUse(true);
}
void loop(void)
{
//Read sensor data
sensors_event_t event;
bno.getEvent(&event);
//Print X, Y And Z orientation
Serial.print("X: ");
Serial.print(event.orientation.x, 4);
Serial.print("\tY: ");
Serial.print(event.orientation.y, 4);
Serial.print("\tZ: ");
Serial.print(event.orientation.z, 4);
Serial.println("");
delay(100);
}

Environment Sensors

Temperature Sensor

A temperature sensor is a device that is used to determine the temperature of the surrounding environment. Most temperature sensors work on the principle that the resistance of the material is changed depending on its temperature. The most common temperature sensors are:

  • thermocouple – consists of two junctions of dissimilar metals,
  • thermistor – includes the temperature-dependent ceramic resistor,
  • resistive temperature detector – is made of a pure metal coil.

The main difference between sensors is the measured temperature range, precision and response time. Temperature sensor usually outputs the analogue value, but some existing sensors have a digital interface [108].

The temperature sensors most commonly are used in environmental monitoring devices and thermoelectric switches. In IoT applications, the sensor can be used for greenhouse temperature monitoring, warehouse temperature monitoring to avoid frozen fire suppression systems and tracking temperature of the soil, water and plants.

 title
Figure 164: Thermistor.
 title
Figure 165: Arduino and thermistor circuit.

An example code:

//Thermistor sensor output is connected to the analog A0 pin
int thermoPin = 0; 
//The analog reading from the thermistor output
int thermoReading;      
 
void setup(void) {
  //Begin serial communication
  Serial.begin(9600);   
  //Initialize the thermistor analog pin as an input
  pinMode(thermoPin, INPUT); 
}
 
void loop(void) {
  //Read the analog value of the thermistor sensor
  thermoReading = analogRead(thermoPin); 
  Serial.print("Thermistor reading = "); //Print out
  Serial.println(thermoReading);
  delay(10);
}
Humidity Sensor

A humidity sensor (hygrometer) is a sensor that detects the amount of water or water vapour in the environment. The most common principle of the air humidity sensors is the change of capacitance or resistance of materials that absorb the moisture from the environment. Soil humidity sensors measure the resistance between the two electrodes. The resistance between electrodes is influenced by soluble salts and water amount in the soil. The output of a humidity sensor is usually an analogue signal value [109].

Example IoT applications are monitoring of humidor, greenhouse temperature and humidity, agriculture, art gallery and museum environment.

 title
Figure 166: Temperature and humidity sensor module.
 title
Figure 167: Arduino Uno and humidity sensor schematics.

An example code [110]:

#include <dht.h>
 
dht DHT;
 
#define DHT_PIN 7
 
void setup(){
  Serial.begin(9600);
}
 
void loop()
{
  int chk = DHT.read11(DHT_PIN);
  Serial.print("Humidity = ");
  Serial.println(DHT.humidity);
  delay(1000);
}
Sound Sensor

A sound sensor is a sensor that detects vibrations in a gas, liquid or solid environments. At first, the sound wave pressure makes mechanical vibrations, who transfers to changes in capacitance, electromagnetic induction, light modulation or piezoelectric generation to create an electric signal. The electrical signal is then amplified to the required output levels. Sound sensors, can be used to record sound, detect noise and its level.

Sound sensors are used in drone detection, gunshot alert, seismic detection and vault safety alarm.

 title
Figure 168: Digital sound detector sensor module.
 title
Figure 169: Arduino Uno and sound sensor schematics.

An example code:

//Sound sensor output is connected to the digital 7 pin
int soundPin = 7; 
//Stores sound sensor detection readings
int soundReading = HIGH; 
 
void setup(void) {
  //Begin serial communication
  Serial.begin(9600);   
  //Initialize the sound detector module pin as an input
  pinMode(soundPin, INPUT); 
}
 
void loop(void) {
  //Read the digital value whether the sound has been detected
  soundReading = digitalRead(soundPin); 
  if (soundPin==LOW) { //When sound detector detected the sound
    Serial.println("Sound detected!"); //Print out
  } else { //When the sound is not detected
    Serial.println("Sound not detected!"); //Print out
  }
  delay(10);
}
Chemical/Smoke and Gas Sensor

Gas sensors are a sensor group, that can detect and measure a concentration of certain gasses in the air. The working principle of electrochemical sensors is to absorb the gas and to create current from an electrochemical reaction. For process acceleration, a heating element can be used. For each type of gas, different kind of sensor needs to be used. Multiple different types of gas sensors can be combined in a single device as well. The single gas sensor output is an analogue signal, but devices with multiple sensors used to have a digital interface.

Gas sensors are used for safety devices, to control air quality and for manufacturing equipment. Examples of IoT applications are air quality control management in smart buildings and smart cities or toxic gas detection in sewers and underground mines.

 title
Figure 170: MQ-7 gas sensor.
 title
Figure 171: Arduino Uno and MQ2 gas sensor schematics.

An example code:

int gasPin = A0; //Gas sensor output is connected to the analog A0 pin
int gasReading; //Stores gas sensor detection reading
 
void setup(void) {
  Serial.begin(9600);   //Begin serial communication
  pinMode(gasPin, INPUT); //Initialize the gas detector pin as an input
}
 
void loop(void) {
  gasReading = analogRead(gasPin); //Read the analog value of the gas sensor
  Serial.print("Gas detector value: "); //Print out
  Serial.println(gasReading);
  delay(10); //Short delay
}
Level Sensor

A level sensor detects the level of fluid or fluidised solid. Level sensors can be divided into two groups:

  • continuous level sensors that can detect the exact position of the fluid. For the level detection usually, the proximity sensors, like ultrasonic or infrared, are used. Capacitive sensors can also be used by recording the changing capacitance value depending on the fluid level. The output can be either analogue or digital value;
  • point-level sensors can detect whether a fluid is above or below the sensor. For the level detection, float or mechanical switch, diaphragm with air pressure or changes in conductivity or capacitance, can be used. The output is usually a digital value that indicates HIGH or LOW value.

Level sensors can be used as smart waste management, for measuring tank levels, diesel fuel gauging, liquid assets inventory, chemical manufacturing high or low-level alarms and irrigation control.

 title
Figure 172: Liquid level sensor.
 title
Figure 173: Arduino Uno and liquid level sensor schematics.

An example code:

int levelPin = 6; //Liquid level sensor output is connected to the digital 6 pin
int levelReading; //Stores level sensor detection reading
 
void setup(void) {
  Serial.begin(9600);   //Begin serial communication
  pinMode(levelPin, INPUT); //Initialize the level sensor pin as an input
}
 
void loop(void) {
  levelReading = digitalRead(levelPin); //Read the digital value of the level sensor
  Serial.print("Level sensor value: "); //Print out
  Serial.println(levelReading);
  delay(10); //Short delay
}

Other Sensors

Hall sensor

A Hall effect sensor detects strong magnetic fields, their polarities and the relative strength of the field. In the Hall effect sensors, a magnetic force influences current flow through the semiconductor material and creates a measurable voltage on the sides of the semiconductor. Sensors with analogue output can measure the strength of the magnetic field, while digital sensors give HIGH or LOW output value, depending on the presence of the magnetic field.

Hall effect sensors are used in magnetic encoders for speed measurements and magnetic proximity switches because it does not require contact, and it ensures high reliability. Example application can be sensing the position of rotary valves.

 title
Figure 174: Hall-effect sensor module.
 title
Figure 175: Arduino Uno and Hall sensor schematics.

Thw example code:

int hallPin = A0; //Hall sensor output is connected to the analog A0 pin
int hallReading; //Stores hallsensor detection reading
 
void setup(void) {
  Serial.begin(9600);   //Begin serial communication
  pinMode(hallPin, INPUT); //Initialize the hallsensor pin as an input
}
 
void loop(void) {
  hallReading = analogRead(hallPin); //Read the analog value of the hall sensor
  Serial.print("Hall sensor value: "); //Print out
  Serial.println(hallReading);
  delay(10); //Short delay
}
Global Positioning System

A GPS receiver is a device, that can receive information from a global navigation satellite system and calculate its position on the Earth. GPS receiver uses a constellation of satellites and ground stations to compute position and time almost anywhere on the Earth. GPS receivers are used for navigation only in the outdoor area because it needs to receive signals from the satellites. The precision of the GPS location can vary.

A GPS receiver is used for device location tracking. Real world applications might be, i.e., pet, kid or personal belonging location tracking.

 title
Figure 176: Grove GPS receiver module.
 title
Figure 177: Arduino Uno and Grove GPS receiver schematics.

The example code [111]:

#include <SoftwareSerial.h>
SoftwareSerial SoftSerial(2, 3);
unsigned char buffer[64];    //Buffer array for data receive over serial port
int count=0;                 //Counter for buffer array
void setup()
{
    SoftSerial.begin(9600);  //The SoftSerial baud rate
    Serial.begin(9600);      //The Serial port of Arduino baud rate.
}
 
void loop()
{
    if (SoftSerial.available())  //If date is coming from software serial port 
                                 // ==> Data is coming from SoftSerial shield
    {
        while(SoftSerial.available())  //Reading data into char array
        {
            buffer[count++]=SoftSerial.read(); //Writing data into array
            if(count == 64)break;
        }
        Serial.write(buffer,count);    //If no data transmission ends, 
                                       //Write buffer to hardware serial port
        clearBufferArray();            //Call clearBufferArray function to clear 
                                       //The stored data from the array
        count = 0;                     //Set counter of while loop to zero 
    }
    if (Serial.available())       //If data is available on hardware serial port 
                                       // ==> Data is coming from PC or notebook
    SoftSerial.write(Serial.read());   //Write it to the SoftSerial shield
}
 
 
void clearBufferArray()                //Function to clear buffer array
{
    for (int i=0; i<count;i++)
    {
        buffer[i]=NULL;
    }                         //Clear all index of array with command NULL
}

Drivers and Driving

Optical Device Drivers and Their Devices

Light-Emitting Diode

The light-emitting diode also called LED is a special type of diodes which emits light, unlike the other diodes. LED has a completely different body which is made of transparent plastic that protects the diode and lets it emit light. Like the other diodes LED conducts the current in only one way, so it is essential to connect it to the scheme correctly. There are two safe ways how to determine the direction of the diode:

  • in the cathodes side of the diode its side is chipped,
  • anodes leg usually is longer than the cathodes leg.
 title
Figure 178: 5 mm Red LED.

The LED is one of the best light sources. Unlike incandescent light bulb LED transforms most of the power into light, not warmth; it is more durable, works for a more extended period and can be manufactured in a smaller size.

The LED colour is determined by the semiconductors material. Diodes are usually made from silicon then LEDs are made from elements like gallium phosphate, silicon carbide and others. Because the semiconductors used are different, the voltage needed for the LED to shine is also different. In the table, you can see with which semiconductor you can get a specific colour and the voltage required to turn on the LED.

When LED is connected to the voltage and turned on a huge current starts to flow through it, and it can damage the diode. That is why all LEDs have to be connected to current limiting resistor.

Current limiting resistors resistance is determined by three parameters:

  • I_D – current that can flow through the LED,
  • U_D – Voltage that is needed to turn on the LED,
  • U – combined voltage for LED and resistor.

To calculate the resistance needed for a diode, this is what you have to do.

  1. Find out the voltage needed for the diode to work UD; you can find it in the diodes parameters table.
  2. Find out the amperage needed for the LED to shine ID; it can be found in the LEDs datasheet, but if you can’t find it then 20 mA current is usually a correct and safe choice.
  3. Find out the combined voltage for the LED and resistor; usually, it is the feeding voltage for the scheme.
  4. Insert all the values into this equation: R = (U – U_D) / I_D.
  5. You get the resistance for the resistor for the safe use of the LED.
  6. Find resistors nominal that is the same or bigger than the calculated resistance.
 title
Figure 179: Arduino Uno and LED control schematic.

The example of the blinking LED code:

int ledPin = 8;//Defining the pin of the LED
 
void setup()
{   
    pinMode(ledPin,OUTPUT); //The LED pin is set to output
}
 
void loop() 
{   
    //Set pin output signal to HIGH – LED is working
    digitalWrite(ledPin,HIGH); 
    //Belay of 1000 ms
    delay(1000); 
 
    //Set pin output signal to LOW – LED is not working
    digitalWrite(ledPin,LOW); 
    //Delay of 1000 ms
    delay(1000);
}
Displays

Using display is a quick way to get feedback information from the device. There are many display technologies compatible with Arduino. For IoT solutions, low power, easy to use and monochrome displays are used:

  • liquid-crystal display (LCD),
  • organic light-emitting diode display (OLED),
  • electronic ink display (E-ink).

Liquid-Crystal Display (LCD)

LCD uses modulating properties of liquid crystal light to block the incoming light. Thus when a voltage is applied to a pixel, it has a dark colour. A display consists of layers of electrodes, polarising filters, liquid crystals and reflector or back-light. Liquid crystals do not emit the light directly; they do it through reflection or backlight. Because of this reason, they are more energy efficient. Small, monochrome LCDs are widely used in devices to show a little numerical or textual information like temperature, time, device status etc. LCD modules commonly come with an onboard control circuit and are controlled through parallel or serial interface.

 title
Figure 180: Blue 16 × 2 LCD display.
 title
Figure 181: Arduino and LCD screen schematics.

The example code:

#include <LiquidCrystal.h> //include LCD library
 
//Define LCD pins
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
//Create and LCD object with predefined pins 
LiquidCrystal lcd(rs, en, d4, d5, d6, d7); 
 
void setup() {
  lcd.begin(16, 2); //Set up the LCD's number of columns and rows
  lcd.print("hello, world!"); //Print a message to the LCD
}
 
void loop() {
  //Set the cursor to column 0, line 1 – line 1 is the second row 
  //Since counting begins with 0
  lcd.setCursor(0, 1); 
  //Print the number of seconds since reset
  lcd.print(millis() / 1000); 
}

Organic Light-Emitting Diode Display (OLED)

OLED display uses electroluminescent materials that emit light when the current passes through these materials. The display consists of two electrodes and a layer of an organic compound. OLED displays are thinner than LCDs, they have higher contrast, and they can be more energy efficient depending on usage. OLED displays are commonly used in mobile devices like smartwatches, cell phones and they are replacing LCDs in other devices. Small OLED display modules usually have an onboard control circuit that uses digital interfaces like I2C or SPI.

 title
Figure 182: OLED I2C display.
 title
Figure 183: Arduino and OLED I2C schematics.
//Add libraries to ensure the functioning of OLED
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
 
void setup() {
  //Setting up initial OLED parameters
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false);
  display.setTextSize(1); //Size of the text
  display.setTextColor(WHITE); //Colour of the text – white
 
void loop() {
 
  //Print out on display output sensor values
  display.setCursor(0, 0);
  display.clearDisplay();
  display.print("Test of the OLED"); //Print out the text on the OLED
  display.display();
  delay(100);
  display.clearDisplay();
}

Electronic Ink Display (E-Ink)

E-ink display uses charged particles to create a paper-like effect. The display consists of transparent microcapsules filled with oppositely charged white and black particles between electrodes. Charged particles change their location, depending on the orientation of the electric field, thus individual pixels can be either black or white. The image does not need the power to persist on the screen; power is used only when the image is changed. Thus e-ink display is very energy efficient. It has high contrast and viewing angle, but it has a low refresh rate. E-ink displays are commonly used in e-riders, smartwatches, outdoor signs, electronic shelf labels.

 title
Figure 184: E-ink display module.
 title
Figure 185: Arduino Uno and E-ink display module schematics.
#include <SmartEink.h>
#include <SPI.h>
 
E_ink Eink;
 
void setup()
{
  //BS LOW for 4 line SPI
  pinMode(8,OUTPUT);
  digitalWrite(8, LOW);
 
Eink.InitEink();
 
Eink.ClearScreen();//Clear the screen
 
Eink.EinkP8x16Str(14,8,"NOA-Labs.com");
Eink.EinkP8x16Str(10,8,"smart-prototyping.com");
Eink.EinkP8x16Str(6,8,"0123456789");
Eink.EinkP8x16Str(2,8,"ABCDEFG abcdefg");
 
Eink.RefreshScreen(); 
}
void loop()
{ 
 
}

Mechanical Drivers

Relay

Relays are electromechanical devices that use electromagnets to connect or disconnect plates of a switch. Relays are used to control high power circuits with low power circuits. Circuits are mechanically isolated and thus protect logic control. Relays are used in household appliance automation, lighting and climate control.

 title
Figure 186: 1 channel relay module.
 title
Figure 187: Arduino Uno and 1 channel relay module schematics.

The example code:

#define relayPin  4 //Define the relay pin
 
void setup()
{    
  Serial.begin(9600);
  pinMode(relayPin, OUTPUT); //Set relayPin to output
 
}
 
void loop()
{
   digitalWrite(relayPin,0);  //Turn relay on
   Serial.println("Relay ON"); //Output text 
   delay(2000); // Wait 2 seconds
 
   digitalWrite(relayPin,1);  //Turns relay off
   Serial.println("Relay OFF");
   delay(2000);
}
Solenoid

Solenoids are devices that use electromagnets to pull or push iron or steel core. They are used as linear actuators for locking mechanisms indoors, pneumatic and hydraulic valves and in-car starter systems.

Solenoids and relays both use electromagnets and connecting them to Arduino is very similar. Coils need a lot of power, and they are usually attached to the power source of the circuit. Turning the power of the coil off makes the electromagnetic field to collapse and creates very high voltage. For the semiconductor devices protection, a shunt diode is used to channel the overvoltage. For extra safety, optoisolator can be used.

 title
Figure 188: Solenoid.
 title
Figure 189: Arduino Uno and solenoid schematics.

The example code:

#define solenoidPin  4 //Define the solenoid pin
 
void setup()
{    
  Serial.begin(9600);
  pinMode(solenoidPin, OUTPUT); //Set solenoidPin to output
 
}
 
void loop()
{
   digitalWrite(solenoidPin,0);  //Turn solenoid on
   Serial.println("Solenoid  ON"); //Output text 
   delay(2000); //Wait 2 seconds
 
   digitalWrite(solenoidPin,1);  //Turns solenoid off
   Serial.println("Solenoid OFF");
   delay(2000);
}
Speaker

Speakers are electroacoustic devices that convert the electrical signal into sound waves. A speaker uses a permanent magnet and a coil attached to the membrane. Sound signal, flowing through the coil, creates the electromagnetic field with variable strength, coil attracts to magnet according to the strength of the field, thus making a membrane to vibrate and creating a sound wave. Other widely used speaker technology, called Piezo speaker, uses piezoelectric materials instead of magnets. Speakers are used to creating an audible sound for human perception and ultrasonic sound for sensors and measurement equipment.

 title
Figure 190: Speaker 8 Ω 0.5 W.
 title
Figure 191: Arduino Uno and piezzo buzzer schematics.
const int speakerPin = 9; //Define the buzzer pin
 
void setup()
{
  pinMode(speakerPin, OUTPUT); //Set buzzer as an output
}
 
void loop()
{ 
  tone(speakerrPin, 1000); //Send 1 kHz sound signal
  delay(1000);        //For 1 s
  noTone(speakerPin);     //Stop sound
  delay(1000);        //For 1 s
}
DC Motor (One Direction)

An electric motor is an electro-technical device which can turn electrical energy into mechanical energy; motor turns because of the electricity that flows in its winding. Electric motors have seen many technical solutions over the year from which the simplest is the permanent-magnet DC motor.

DC motor is a device which converts direct current into the mechanical rotation. DC motor consists of permanent magnets in stator and coils in the rotor. By applying the current to coils, the electromagnetic field is created, and the rotor tries to align itself to the magnetic field. Each coil is connected to a commutator, which in turns supplies coils with current, thus ensuring continuous rotation. DC motors are widely used in power tools, toys, electric cars, robots, etc.

 title
Figure 192: A DC motorwith gearbox 50 : 1.
 title
Figure 193: Arduino Uno and DC motor schematics.
void setup ()
{
  pinMode(5,OUTPUT); //Digital pin 5 is set to output
  //The function for turning on the motor is defined
  #define motON digitalWrite(5,HIGH) 
  //The function for turning off the motor is defined
  #define motOFF digitalWrite(5,LOW)  
}
void loop ()
{
  motON; //Turn on the motor
}
DC Motor With H-Bridge

The H-bridge has earned its name because of its resemblance to the capital ‘H’ wherein all the corners there are switches and in the middle – the electric motor. This bridge is usually used for operating permanent-magnet DC motor, electromagnets and other similar elements, because it allows operating with significantly bigger current devices, using a small current. By switching the switches, it is possible to change the motor direction. It is important to keep in mind that the switches need to be turned on and off in pairs.

When all of the switches are turned off, the engine is in the free movement. To slow down faster, the H-bridge is turned on in the opposite direction.

 title
Figure 194: The flow of currents in the H-bridge.

If both positive or both negative switches are turned on at the top or at the bottom, then the engine stops, not allowing to have a free rotation – it is slowed down. The management can be reflected in Table 10.

When all of the switches are turned off, the engine is in the free movement. Not always it is enough for robotics, so sometimes the H-bridge is turned on in the opposite direction to slow the motor down faster – the opposite direction is turned on rapidly.

Table 17: The Management of the H-Bridge Switches
Upper left Upper right Lower left Lower right Motor work mode
On Off Off On Turns in one direction
Off On On Off Turns in another direction
On On Off Off Braking
Off Off On On Braking

Remember! Neither of these braking mechanisms is good for the H-bridge or the power source. That is why this action is unacceptable without a particular reason because it can damage the switches or the power source.

The complicated part is the realisation of switches – if the switches don’t work usually relays or appropriate power transistors are used. The biggest drawback for relays is that they can only turn the engine on or off. If the rotation speed needs to be regulated using the impulse width modulation, then transistors have to be used. MOSFET type transistors should be used for ensuring a large amount of power. Nowadays, the stable operation of the bridge is ensured by adding extra elements. The manufactured bridges have one body, for example, the one that is included in the constructor – L293D.

 title
Figure 195: The L293D microchip and its representation in the circuit.

The L293D microchip consists of two H-bridges and is made for managing two motors. Each pin of the microchip has its function; that is why it is very important not to mix them up; otherwise, the microchip can be damaged. All pins of the microchip have assigned a number. The enumeration begins with the special mark on the body: a split, a dot, a cracked edge, etc., and continues counter-clockwise. When creating a scheme, it is important to take into account pin numbers and the ones shown in the scheme. If some additional information about the microchip is necessary, it can be found in the datasheet of the microchip. Remember that the datasheet can be found by writing the number of the device (written on the body) and adding the word “datasheet” in the browser.

 title
Figure 196: Arduino Uno and L293D H-bridge schematics.

The example code:

int dirPin1 = 7;    //1st direction pin
int dirPin2 = 8;    //2nd direction pin
int speedPin = 5;   //Pin responsible for the motor speed
 
void setup ()
{
  pinMode (dirPin1,OUTPUT); //1st direction pin is set to output
  pinMode (dirPin2,OUTPUT); //2nd direction pin is set to output
  pinMode (speedPin,OUTPUT); //Speed pin is set to output
}
 
void loop ()
{
  analogWrite(speedPin, 100); //Setting motor speed
  //Speed value can be from 0 to 255
 
  int motDirection = 1; //Motor direction can be either 0 or 1
 
  if (motDirection) //Setting motor direction
  {//If 1
    digitalWrite(dirPin1,HIGH);
    digitalWrite(dirPin2,LOW);
  }
  else
  {//If 0
    digitalWrite(dirPin1,LOW);
    digitalWrite(dirPin2,HIGH);
  }
}
Stepper Motor

Stepper motors are motors, that can be moved by a certain angle or step. Full rotation of the motor is divided into small, equal steps. Stepper motor has many individually controlled electromagnets, by turning them on or off, a motor shaft rotates by one step. Changing switching speed or direction can precisely control turn angle, direction or full rotation speed. Because of exact control ability, they are used in CNC machines, 3D printers, scanners, hard drives etc. Example of use can be found in the source [112].

 title
Figure 197: A stepper motor.
 title
Figure 198: Arduino Uno and stepper motor schematics.

The example code:

#include <Stepper.h> //Include library for stepper motor
 
int in1Pin = 12; //Defining stepper motor pins
int in2Pin = 11;
int in3Pin = 10;
int in4Pin = 9;
 
//Define a stepper motor object
Stepper motor(512, in1Pin, in2Pin, in3Pin, in4Pin);  
 
void setup()
{
  pinMode(in1Pin, OUTPUT); //Set stepper motor control pins to output
  pinMode(in2Pin, OUTPUT);
  pinMode(in3Pin, OUTPUT);
  pinMode(in4Pin, OUTPUT);
 
  Serial.begin(9600);
  motor.setSpeed(20); //Set the speed of stepper motor object
}
 
void loop()
{
    motor.step(5); //Rotate 5 steps
}
Servomotor

Unlike the simple DC motor, the servomotor is a particular management chain which allows effortless control over the speed or position of the motor. The management of the engine is realised using three connections – positive (usually red) and negative connection (brown or black) of the current as well as the connection for management (orange or yellow).

For the management, the pulse width technique is used.

 title
Figure 199: The pulse width modulated signal for the management of servomotor.

From the image, it can be seen that the length of the servomotor impulse cycle is 20 ms, but the impulse length itself is 1 ms or 2 ms. These signal characteristics are true for the most enthusiast level servomotors, but it should be verified for each module in the manufacturer specification. Servomotor management chain meets the impulse every 20 ms, but the width of the pulse shows the position that the servomotor has to reach. For example, 1 ms corresponds to the 0° position but 2 ms – to 180° position against the starting point. When entering the defined position, the servomotor will keep it and resist any outer forces that are trying to change the current position. The graphical representation is in the following image.

 title
Figure 200: The pulse width modulated signal for different positions of servomotor.

Just like other motors, servomotors have different parameters, where the most important one is the time of performance – the time that is necessary to change the position to the defined position. The best enthusiast level servomotors do a 60° turn in 0.09 s. There are three types of servomotors:

  • positional rotation servomotor – most widely used type of servomotor. With the help of a management signal, it can determine the position of the rotation axis from its starting position;
  • continuous rotation servomotor – this type of motor allows setting the speed and direction of the rotation using the management signal. If the position is less than 90°, it turns in one direction, but if more than 90° it turns in the opposite direction. The speed is determined by the difference in value from 90°; 0° or 180° will turn the motor at its maximum speed while 91° or 89° at its minimum rate;
  • linear servomotor – with the help of additional transfers it allows moving forward or backward; it doesn’t rotate.

Unfortunately, using Arduino, the servomotor is not as easily manageable as DC motor. For this purpose, a special servomotor management library Servo.h has been created.

 title
Figure 201: A servomotor.
 title
Figure 202: Arduino Uno and servomotor schematics.

The example code:

#include <Servo.h> //Include Servo library
Servo servo; //Define a Servo object
 
void setup ()
{
  servo.attach(6); //Connect servo object to pin D6
  servoLeft.write(90); //Set position of servo to 90°
  Serial.begin(9600);
}
 
void loop ()
{
  servoLeft.write(110); //Set position of servo to 110°
  delay(200); //wait for 200 ms
  servoLeft.write(70);//Set position of servo to 70°
  delay(200); //Wait for 200 ms
}

IoT Application Example Projects

Many IoT projects developed using an Arduino board can be found in the official Arduino Project Hub [113]. Here are stored multiple projects that are developed by Arduino enthusiasts. In many of the following examples, the Arduino Yun board is used, because it is easy to use a controller that contains the WiFi connection that is necessary for IoT solutions. Additionally, the Amazon services are used for storing and handling the sensor data.

One of the IoT projects available at the Arduino Project Hub is the Arduino Home Controller Activated by Alexa [114] (Alexa is the Amazon Echo dot [115]) that uses an Arduino Yun controller. In this solution, it is possible to control internet-connected devices around the home using voice, using Alexa and Amazon Web Service (AWS). Natural language voice commands are interpreted using Amazon Skill and a Lambda Function in the AWS. The developed system gives an opportunity to control four lights installed in the room, garage, kitchen and living room, temperature and humidity, buzzer alarm and WebCam that takes a security photo and sends it by e-mail.

The Home Security Model [116] is another example to monitor the security status in the real-time using sensors and internet connection. The system data can be accessed and controlled remotely using a smart device or a PC. In this project, the Arduino Yun and Arduino Mega controllers are used. The AWS IoT services are used for connecting the devices around the home. The system includes temperature and humidity, gas, water, vibration, current and ultrasonic sensors, and it controls the light in multiple rooms and a buzzer.

The project Plant Monitoring System uses AWS IoT [117] and gives the opportunity to track the temperature and soil moisture of the plant using AWS IoT. Using the Arduino Yun controller, the system monitors such parameters as the temperature, soil moisture and light value. These log data are stored in the AWS IoT service. Additionally, the Amazon Simple Notification Service (SNS) is used for sending notification informative e-mails about the status of the sensor measurements.

Here is another IoT project about the home automation system, called Arduino based Amazon Echo using 1Sheeld [118]. 1Sheeld is a hardware shield that allows using sensors of the smartphone as the inputs for Arduino. In this project, a smartphone is used to play music (Music Player Shield), interact with the person by responding to questions (Text to Speech Shield), return the real-time (Clock Shield), return the information about the weather (Internet Shield) and it is controlled by voice commands (Voice Recognition Shield).

Few of the Arduino IoT related projects are also provided on the hackster.io website [119].

The first project that is viewed is the Harry Potter Weasleys' Clock using Bolt IoT [120]. The idea of this project is taken from the Harry Potter movie where the wall clock indicated the location of family members. In this modern IoT project, using Arduino Uno microcontroller, servomotor and Bolt WiFi Module, the clock that using arrow represents the location of the person that has a smartphone has been developed. The clock and the tracked smartphone are connected to the Bolt cloud server [121].

Setting Up Development/Experimenting Environment

Using Breadboard

The breadboard is a mechanical base for electronics prototyping. Originally for this purpose was used the wooden board for cutting bread, that is why the name used now is the “breadboard”, also know as a solderless breadboard. It is because an electrical connection on this board can be made without soldering. Thus components can be reused, and changes to the circuit can be made easily.

 title
Figure 203: 400 point Breadboard.

On the front surface of the breadboard, many small holes are connected on the back of the board in a specific pattern using metal clips. These clips hold component leg or wire in place when they are put into the hole.

As the example, the circuit that contains the LED and resistance is taken.

 title
Figure 204: The example schematics.

Following the schematics, all the necessary components are connected in the right way using the breadboard.

 title
Figure 205: Example circuit connected using breadboard.

Two side columns of the breadboard are made easily accessible from the rest of the breadboard and are commonly used for connecting the power to the circuit. Almost any DC (direct current) power source can be used to power the circuit, like batteries, Arduino board power pins, AC/DC regulators etc.

Two columns of 5 hole rows are used for connecting components. Extra connections can be made using wires. The gap in the middle allows using DIP (dual in-line package) circuits.

More information on breadboards can be found in the SparkFun webpage [122].

Soldering

Soldering is one of the essential skills in the world of electronics. The basic of electronics can also be a learner without the knowledge of how to solder; however, the soldering allows to work on more exciting projects and to join a wide range of electronics enthusiasts. This skill is essential because nowadays the electronic and electrical equipment is being used more often. The essential element of the knowledge about electronics is not only the ability to understand the working principles of the electronics but also the skill to build, repair and supplement the electronic devices.


Materials

The main soldering material is a solder. As the name of the material indicates, it is a compound of various soft metals and consumable materials, which is usually similar to a wire, wrapped in a reel or other easy-to-use packaging.

 title
Figure 206: Different packaging of solder.

Different types of solder vary in diameter of the wire and the chemical composition. The type of solder that is used depends on the task.

According to the chemical composition, the solder is divided into two types: solder containing lead and lead-free solder. Historically, lead (Pb) is used in combination with tin (Sn) to ensure lower melting temperatures and better flow, which is essential for good contact between the parts.

Since 2006, many countries have forbidden using lead-containing solder, caring for the protection of nature and human health. When lead accumulates in the human body in significant amounts, it can cause poisoning. For this reason, it is important to remember – after using lead-containing solder, you should carefully wash your hands.

To avoid the risk of getting lead in the human body, the alternative is to use lead-free solder. Nevertheless few things need to be taken into account – the melting temperature of lead-free solder is higher and grip with other materials is lower. For improving the grip, flux can be used. Some solders already have flux in the core of the wire, so it is not necessary to buy them separately.

The diameter of the solder wire depends on the size of details that need to be soldered. The bigger the detail, the bigger the diameter of the solder wire.

Tools

Tools used for soldering are a soldering iron, stand for soldering iron, as well as different tools for removing solder and keeping parts together.

A soldering iron is an electrical heater that is used for heating the solder to it's melting temperature. As with all of the electrical devices, the instructions provided by the manufacturer should be followed when using the soldering iron. For specific tasks, there are also hot air and gas soldering irons. In this section, only the electric ones will be described.

There are different types of soldering irons so that they can be effectively used for diverse tasks. Soldering irons can vary in a shape of the tip, electrical capacity, heating temperature and control options. However, the primary indicator is the convenience of use. If the chosen soldering iron is too big, it will be difficult to solder small details. If it is too small, details probably will not get enough of the heat, and the solder will not stick properly to them. For beginners, it is advisable to use a soldering iron with a conical tip.

Figure 207: A soldering iron with the conical tip.

In addition to the soldering iron, there are several useful tools for soldering.

  • Solder wick – used for desoldering, removes the extra solder out of the board or details, it absorbs the melted solder. The solder wick consists of multiple fine, interlaced lead threads.
Figure 208: Solder wick.
  • Solder vacuum – used for desoldering, removes the extra solder utilising a vacuum.
Figure 209: Solder vacuum.
  • Third-hand – a holder of details or other parts. It is especially useful for beginners.
Figure 210: Third hand with the magnifying glass.

Process of Soldering

It is not easy to describe the process of soldering in words, because it is connected with the kinesthetic skills of people and the paid attention. However, it is essential to follow several pieces of advice for good soldering.

  1. Be careful with the hot soldering iron!
  2. The working place should always be clean; it is advisable not to take a meal at the working place (because of the poisonous nature of the lead in the solder).
  3. It is advisable to use the third hand when it is possible.
  4. If possible, the temperature of the soldering iron should always be around 350 °C.
  5. If there is smoke coming from the soldering iron, the temperature should be decreased, or it should be turned off completely.
  6. Before soldering, a special soldering iron cleaner (wet sponge or special paste for cleaning the tip) should be used.
  7. The side of the soldering iron tip should be used when soldering rather than the very tip of it.
  8. The good contact can be ensured when heating simultaneously both components that are soldered.
  9. When the detail has been soldered, first the solder wire should be taken off and only then – the soldering iron.
  10. Good soldering of the detail with the board looks like a tip of the volcano rather than a ball or messy pile of solder.

In the Figure 211, the correct and incorrect soldering techniques are indicated.

Figure 211: A visualization of the soldering technique [123].

Arduino Programming Fundamentals

The following sub-chapters cover programming fundamentals in Arduino(C) C/C++, which complies with the most C/C++ notations and have some specific Arduino notations. Those who feel comfortable in programming will find these chapter somewhat introductory, while for these having no or little experience, it highly recommended covering this introduction. This chapter and its sub-chapters also target ESP and Raspberry Pi devices on the general level partially, however, in any case, programming environment configuration is different for every platform even, if Arduino IDE constitutes the joint part. Refer to the chapters on ESP and Raspberry Pi hardware platforms:

This manual refers to the particular version of the software (here Arduino IDE and related toolkits) available at the moment of writing of this book, thus accessing specific features may change over time along with the evolution of the platform. Please refer attached documentation (if any) and browse Internet resources to find lates guidances on how to configure development platform, when in doubt.

Setting Up the Programming Environment

Before starting programming the microcontroller, it is necessary to connect it to the computer.

Connection

Arduino Uno microcontroller is taken as a board for programming example tasks. It can be connected to a computer, using Universal Serial Bus (USB) port, using the appropriate USB cable. A microcontroller can be used together with a prototyping board or a robot. In the simplest programming tasks, it can be used as an independent device.

Power

The microcontroller has to be powered via an external power supply or USB port. The microcontroller determines the power source automatically. If external power supplies other than USB are used, GND and VIN ports should be used to connect the power supply. The manufacturer recommends the use of a voltage of 7–12 V to ensure a normal operation of the microcontroller. If the voltage is exceeded, before reaching 20 V, then the power supply circuits of the microcontroller may get overheated. If the supply voltage is lower than 7 V, then the microcontroller may function unstable, and the result will be unpredictable.

In addition to the above mentioned, the microcontroller can provide a small power supply for external circuits by connecting them according to the microcontroller pins.

Table 18: The Power Pins of Arduino UNO
Pin Description
VIN The input of a power supply when a USB port is not used, i.e., an external power supply is used
5V A regulated 5 V power supply, which can be provided via both USB and VIN
3.3V A 3.3 V supply voltage for external circuits. The maximum current that this output can provide is 50 mA. If it is exceeded, the power supply circuits of the microcontroller may be permanently damaged
GND Ground or port 0
Inputs/Outputs

Each of the 14 digital inputs/outputs (I/O) of the microcontroller can be used to send or receive signals using the pinMode(), digitalWrite() and digitalRead() commands, which will be more detailed discussed in the chapter about the basics of programming. All I/O operate in the range of 0 V to 5 V. Each of the I/O is capable of receiving or sending no more than 40 mA of current. They all have internal load resistors in the range of 20–50 kΩ.

Descriptions of other microcontroller pin and their specific use are explained below. In addition to these I/O, the microcontroller also provides other specific functions that will be described below.

Table 19: Specific I/O Pins of Arduino UNO
Pin Description
0(RX) and 1(TX) Serial I/O for serial communication. RX is used for receiving data, and TX for sending data to external devices. For data transmitting and receiving, the voltage must not exceed 5 V
2 and 3 External interrupt pins that can be used to receive an external interrupt in cases when the value is low, the value is changed, etc. For this functionality the function attachInterrupt() is used
PWM: 3, 5, 6, 9, 10, 11 Pulse Width Modulation (PWM) pins are used to provide 8-bit PWM signal that often can be used for motor control or other specific use cases. For this functionality the analogWrite() function is used
SPI: 10(SS), 11(MOSI), 12(MISO), 13(SCK) Pins that support Serial Peripheral Interface (SPI) communications. For this feature, the SPI library is used
LED: 13 This pin is used to manage the built-in LED. LED can be turned on by setting the value of pin HIGH and turned off by setting pin value LOW
A4(SDA) and A5(SCL) Two Wire Interface (TWI) pins, Serial Data Line (SDA) and Serial Clock Line (SCL), are the alternative of the data exchange using serial communication. For supporting TWI, the Wire library should be used
AREF It is the reference voltage for the analogue inputs. For this functionality analogReference() is used
Reset Gives the opportunity to reset the microcontroller by setting this pin to LOW
Installing the Programming Environment

To start the development of software for a microcontroller, it is necessary to install and properly configure the development environment that consists of the program editor and the Arduino UNO driver. Below are described all the steps that are needed to prepare the programming environment for Windows 10 OS.

Step 1. Preparing Arduino UNO and the USB Cable

Before installing the programming environment, it is necessary to prepare the Arduino UNO board and the USB cable for connecting the board to the computer.

 title
Figure 212: The Arduino UNO board.
 title
Figure 213: USB B cable for Arduino UNO.

Step 2. Downloading the Arduino Software Development Environment

The open-source Arduino Software (Integrated development environment (IDE)) can be found in the official Arduino website [124]. The appropriate installation file depends on the OS of the computer and the access rights of the user.

 title
Figure 214: Downloading the installation file for Windows OS from Arduino original website.

For Windows OS, the Windows Installer should be clicked, and then the file should be saved on the computer. When the installation file has downloaded, the file should be run. If the ZIP file was downloaded, it is necessary to unarchive it and to run the installer. Follow the instructions of the installer. If the operating system asks for permission to install the driver of the board – allow it.

It is also possible to use Arduino Web Editor (can be found on the same website) to work online with the Arduino board, but this option will not be considered in this manual.

 title
Figure 215: Arduino Web Editor.

Step 3. Connecting to Arduino

Using USB cable, Arduino needs to be connected to a free USB port of a computer. The blue LED on the Arduino board starts to shine continuously. Aforementioned is the indicator that the Arduino board is working.

The green LED will blink, and that will indicate the performance of the manufacturer test software. In case if the green LED is not flashing, it is not an error.

Step 4. Starting Up the Programming Environment

The Arduino programming environment can be started with the double-click on the desktop shortcut of the Arduino software. The language of the environment will respond to the one that is set up in the OS of the computer, that means if it is English, then the menu of the programming environment will also be in the English language. To change the language preferences, it is necessary to follow the instructions in the following webpage [125].

Step 5. Open the Example Program

In the Arduino IDE open File → Examples → 01.Basics → Blink as shown in the image below.

 title
Figure 216: The path to open Blink example in the Arduino IDE.

This will open in the new window an example program for turning on and off green LED that is situated on the Arduino UNO board with the 1 second delay.

 title
Figure 217: The example Blink program.

Step 6. Choosing the Microcontroller

In this step it is necessary to choose the type of board that is used. In this example the Arduino UNO board is used that is why in the menu of Arduino IDE choose Tools → Board → Arduino/Genuino Uno as shown in the image below.

 title
Figure 218: The path to choose the type of board.

Step 7. Setting Up COM Port

To ensure transmitting and receiving data to/from the microcontroller, it is necessary to set the serial communication port – COM port. All ports are numbered in order, and for Arduino microcontroller, it is usually higher than COM3, i.e. COM4, COM5, etc. In the image below, it is COM4.

 title
Figure 219: The path to choose the port for Arduino connection.

Step 8. Uploading the Example Program to the Board

Now the program can be uploaded to the Arduino board using the Upload button in the top left corner of the software, then wait for a few seconds, during which you can see the data sending indicators – LEDs are blinking fast (indicates sending or receiving data) – and wait for the message to be “Upload is complete”.

 title
Figure 220: Uploading program to the board.

After a few seconds, the green LED will blink with a one-second interval like it is written in the source code. If this can be observed successfully, then everything is done to start learning the basics of programming.

In case if the blinking green LED cannot be observed, instructions for troubleshooting can be read in the following link [126].

If you want to get acquainted yourself with microcontroller capabilities or programming basics independently, look at one of these sources of information:

  • examples for the accomplishing tasks of different level of difficulty [127];
  • reference for the programming language used [128].

Check Yourself

1. What power supply Arduino UNO microcontroller requires?

2. How to operate with inputs/outputs of the microcontroller?

3. Try different examples in the menu of Arduino IDE.

The Syntax and the Structure of the Program

Syntax

Arduino IDE is a software that allows writing Arduino code. Each file with Arduino code is called a sketch. The Arduino programming language is similar to the C++ language. For the Arduino IDE to compile the written code without errors, it is important to follow the pre-defined syntax.

Define

#define is a component that allows giving a name to a constant value at the very beginning of the program.

#define constant 4

In this example, the value four is assigned to the constant.

Note that at the end of this expression semicolon (;) is not necessary and between the name and the value, the sign of equality (=) should not be added!

Include

#include is a component that allows to include libraries from the outside of the program. Just like the #define, #include is not terminated with the semicolon at the end of the line!

#include <Servo.h>

In this example, the library called Servo.h that manages servomotors has been added to the sketch.

Comments

There are two ways to write comments in the sketch so that the written text is not compiled as a part of the running code.

//Single line comment is written here

The double backslash is used when the only single line should be commented on.

/*Multi-line comments are written here
Second line of the comment
...
*/

In this way, the backslash followed by asterisk defines the beginning of the block comment, and the asterisk followed by backslash defines the end of the block comment.

Semicolon

The semicolon (;) is used at the end of each statement of the code.

int pin = 5;

In this example, a single statement is int pin = 5 and the semicolon at the end tells the compiler that this is the end of the statement and it can continue with the next one.

Curly Braces

Curly braces are used to enclose a further block of instructions. Curly braces usually follow different functions that will be viewed in the further sections.

Each opening curly brace should always be by a closing curly brace. Otherwise, the compiler will show an error.

void function(datatype argument){
  statements(s)
}

Use of curly braces in the own defined function.

Structure of the Program

Below is given an example, how a new empty sketch looks like.

title
Figure 221: The empty sketch in the Arduino IDE.

Each Arduino sketch contains multiple parts.

  1. Global definition section – the section where variables and constants are defined that the working range is in the whole sketch that means both in the initialisation and the loop sections. This section is at the very beginning of the sketch, before the setup function.
  2. Initialisation section – is executed only once before running the basic program. In this part usually, all variables, I/O of the board pins, constants, are defined, etc. The most essential is to define all inputs and outputs that will be used in the program that defines how each pin will be used.

    The construction of the setup function:
    void setup()   //The result data type and the name of the function
    {              //Beginning of the initialization function
                   //The body of the function - contains all executable statements
    }              //The end of the initialization function

    As it was already mentioned, this function will execute only once. The function is described by the result data type (number, symbol array or something else). In this example, the keyword setup means that the setup function does not have the result that means it is executed only once, and that is all. The name necessarily should be setup, so that the built-in subsystem of the board program execution could differ the initialisation section from the rest of the code.

  3. Loop section – the part that is executed continuously, it reads inputs, processes data and gives output signals. After running the last statement of the function, the program continues again with the first statement of the same loop function. This is the main part of the program execution that encloses all the steps of program execution, including the logic.

    The construction of the loop function is the following:
    void loop()     //The result data type and the name of the function
    {               //Beginning of the loop function
       //Logic      //The body of the function - contains all executable statements
    }               //The end of the loop function

The result data type of this function is the same as previous – void – that shows that the function does not have the result, it will be executed in the loop continuously while the program is working.

The code of the Blink LED program code will be viewed now. The example can be opened by following the path in Arduino IDE: File → Examples → 01.Basics → Blink.

 title
Figure 222: The path to open the Blink LED example program.

When the Blink LED example program is opened, the following sketch should open in the programming environment:

 title
Figure 223: The Blink LED example program.

The code of the example program is the following:

//The setup function runs once when you press reset or power the board
void setup() {
  //Initialize digital pin LED_BUILTIN as an output. 
  //LED_BUILTIN stands for the built-in LED on the board.
  pinMode(LED_BUILTIN, OUTPUT);
}
 
//The loop function runs over and over again forever
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);  //Turn the LED on (HIGH is the voltage level)
  delay(1000);                      //Wait for a second
  digitalWrite(LED_BUILTIN, LOW);   //Turn the LED off by making the voltage LOW
  delay(1000);                      //Wait for a second
}

In the source code of the program, the following things can be seen.

  1. It defined that the LED_BUILTIN is set to be the output of the program. In this example sketch, the output periodically sends the specific signal that is in the level of the logical 1 (+5 V) and 0 (0 V). Sending the output signal to the built-in LED: the LED is periodically turned on and off.
  2. Continuous executable function loop() is created that allocates 1 second (1000 ms) of time to each level of signal. It is done by pausing the execution of the program. While the program is not changing the states of the inputs/outputs, they remain unchanged. In this way, when the +5 V signal is sent to the LED output, and the program execution is paused, the LED will continue to shine until the level of the output will be set to 0 V.
  3. The last row indicates that the program will be paused for a 1 second also when the output level is set to be 0 V. In this way, the period of LED on and off are equal. After executing this program, the program returns to the first line of the loop() function, and the execution starts from the beginning.
Hello World

Hello World program is the simplest program because it simply outputs the text to the screen. Here is the Hello World program for Arduino that outputs the text on the Serial Monitor each second:

void setup() {
  Serial.begin(9600); //Establishes the connection with the serial port
}
 
void loop() {
  Serial.println("Hello World"); //Prints out the line with the text
  delay(1000);                   //Pause for 1 second
}

Serial Monitor can be found following the path: Tools → Serial Monitor.

In the code can be seen that the setup() function contains the following command:

Serial.begin(9600);

This statement opens the serial port at the initialisation of the program so that the Serial Monitor can be used for outputting text or values on the screen.

For printing out text the following command is used:

Serial.println("Hello World");

Check Yourself

1. How to attach any library to a sketch?

2. What command are expressions not usually separated by the semicolon?

3. How to establish serial communication between devices?

4. How does delay() command works?

  • Stops LED blinking specified number of milliseconds.
  • Stops program execution for a specified number of seconds.
  • Stops program execution for a specified number of milliseconds.

Data Types and Variables

Data Types

Each variable has its data type that determines its place in the memory of the microcontroller and also the way how it can be used. There are plenty of different data types. Further will be viewed the most used ones.

  • byte – numeric type of 8 bits that stores numbers from 0 to 255.
byte exampleVariable = 123;
  • int – the whole number of 16 bits that can contain values from –32 767 to 32 768.
int exampleVariable = 12300;
  • float – a data type for real numbers that uses 32 bits and store numbers approximately from –3.4 × 10^38 to 3.4 × 10^38.
float exampleVariable = 12300.546;
  • array – a set of the same data type that can be accessed using serial number or index. The index of the first element is always 0. The values of an array can be initialised at the definition of to do it during the execution of the program. In the following example the array with the name “first array” and data type int has been created. The value of the array with the index 0 will be 12, and the value with the index 3 is 15.
int firstArray[] = {12,-3,8,15};

Square brackets of the array can be used to access some value in the array by index. In the following example, the element with index 1 (that is –3) is assigned to the secondVariable variable.

int secondVariable = firstArray[1];

An array can be easily processed in the cycle. The next example shows how to store the necessary values automatically from the analogue signal input to the previously defined array.

//The cycle that repeats 4 times
for(int i = 0; i < 4; i = i + 1){         
     //Reads value from the pin 2 and stores it in the //exampleArray//.	
     exampleArray[i] = analogRead(2); 
}

This cycle in the example starts with index 0 (i = 0), and it increases by 1 while it is smaller than 4 (not including). That means in the last cycle the index value will be 3, because when the i equals 4, the inequality i < 4 is not true, and the cycle stops working.

  • bool – the variables of this data type can take values TRUE or FALSE. Arduino environment allows following values to these variables: TRUE, FALSE, HIGH (logical 1 (+5 V)) and LOW (logical 0 (0 V)).
Data Type Conversion

Data type conversion can be done using multiple techniques – casting or data type conversion using specific functions.

  • Casting – cast operator translates one data type into another straightforward. The desired type of variable should be written in the brackets before the variable data type of which needs to be changed. In the following example, where the variable type is changed from float to int, the value is not rounded but truncated. Casting can be done to any variable type.
int i;
float f=4.7;
 
i = (int) f; //Now i is 4
  • Convertingbyte(), char(), int(), long(), word(), float() functions are used to convert any type of variable to the specified data type.
int i = int(123.45); //The result will be 123
  • Converting String to float – function toFLoat() converts String type of variable to the float. In the following example is shown the use of this function. In case if the value cannot be converted, because the String doesn't start with a digit, the returned value will be 0.
String string = "123fkm";
float f = string.toFLoat(); //The result will be 123.00
  • Converting String to Int – function toInt() converts String type of variable to the Int. In the following example is shown the use of this function.
String string = "123fkm";
int i = string.toInt(); //The result will be 123
Arithmetic Operators

Arithmetic operations on Arduino are used to do mathematical calculations with the numbers or numerical variables. The arithmetic operators are the following.

  • Addition (+) – one of the four primary arithmetic operations that are used to add numbers. Addition operator can be used to add only numbers, only numeric variables or the mix of both. The following example shows the use of the addition operator.
int result = 1 + 2; //The result of the addition operation will be 3
  • Subtraction (-) – the operation that subtracts one number from another where result is the difference between these numbers.
int result = 3 - 2; //The result of the subtraction operation will be 1
  • Multiplication (*) – the operation that multiplies numbers and gives the result.
int result = 2 * 3; //The result of the multiplication operation will be 6
  • Division (/) – the operation that divides one number by another. If the result variable has the integer type, the result will always be the whole part of the division result without the fraction behind it. If the precise division is necessary, it is important to use float type of variable for this purpose.
//The result of the division operation will be 3 
//(Only the whole part of the division result)
int result = 7 / 2;        
//The result of the division operation will be 3.5
float result2 = 7.0 / 2.0; 
  • Modulo (%) – the operation that finds the remainder of the division of two numbers.
//The result of the modulo operation will be 1, 
//Because if 7 is divided by 3, the remaining is 1
int result = 7 % 3; 
  • Assignment operator (=) – the operator that assigns the value on the right to the variable that is on the left of the assignment operator. The work of an assignment operator can be seen in any of the previously viewed operation examples.
Compound Operators

Compound operators in Arduino are a short way of writing down the arithmetic operations with variables. All of these operations are done on integer variables. These operands are often used in the loops when it is necessary to manipulate with the same variable in each iteration of the cycle. The compound operators are the following.

  • Increment (++) – increases the value of integer variable by one.
int a = 5;
a++; //The operation a = a + 1; the result will be 6
  • Decrement (- -) – decreases the value of integer variable by one.
int a = 5;
a--; //The operation a = a – 1; the result will be 4
  • Compound addition (+=) – adds the right operand to the left operand and assigns the result to the left operand.
int a = 5;
a+=2; //The operation a = a + 2; the result will be 7
  • Compound subtraction (-=) – subtracts the right operand from the left operand and assigns the result to the left operand.
int a = 5;
a-+3; //The operation a = a – 3; the result will be 2
  • Compound multiplication (*=) – multiplies the left operand by the right operand and assigns the result to the left operand.
int a = 5;
a*=3; //The operation a = a × 3; the result will be 15
  • Compound division (/=) – divides the left operand with the right operand and assigns the result to the left operand.
int a = 6;
a/=3; //The operation a = a / 3; the result will be 2
  • Compound modulo (%=) – takes modulus using two operands and assigns the result to the left operand.
int a = 5;
//The result will be the remaining 
//Part of the operation a/2; it results in 1
a%=2; 
  • Compound bitwise OR (|=) – bitwise OR operator that assigns the value to the operand on the left.
int a = 5;
a|=2; //The operation a=a|2; the result will be 7
  • Compound bitwise AND (&=) – bitwise AND operator that assigns the value to the operand on the left.
int a = 6;
a&=; //The operation a=a&2; the result will be 2

Check Yourself

1. What is the data type used for a variable range 0…255?

2. What should data type be used for more precise measurements?

3. Make data type conversion char to string.

4. What is the main advantage of using compound operators?

Program Control Structures

Control Structure

if is a statement that checks the condition and executes the following statements if the condition is TRUE. There are multiple ways how to write down the if statement:

//1st example
if (condition) statement;     
 
//2nd example
if (condition)
statement;                    
 
//3rd example
if (condition) { statement; } 
 
//4th example
if (condition)
{
  statement;
}                             

When both TRUE and FALSE cases of the condition should be viewed, the else part should is added to the if statement in the following ways:

if (condition) {
  statement1;    //Executes when the condition is true
}
else {
  statement2;    //Executes when the condition is false
}

If more conditions should be viewed, the else if part is added to the if statement:

if (condition1) {
  statement1;    //Executes when the condition1 is true
}
else if (condition2) {
  statement2;    //Executes when the condition2 is true
}
else {
  statement3;      //Executes in all of the rest cases
}

The example when the x variable is compared and in the cases when it is higher than 10, the digitalWrite() method executes.

if (x>10) 
{
  //Statement is executed if the x > 10 expression is true
  digitalWrite(LEDpin, HIGH)  
}
Logical Operators

Logical operators are widely used together with the condition operator if that is described below.

Comparison Operators

There are multiple comparison operators used for comparing variables and values. All of these operators compare the value of the variable on the left to the value of the variable on the right. Comparison operators are the following:

  • == (equal to) – if they are equal, the result is TRUE, otherwise FALSE;
  • != (not equal to) – if they are not equal, the result is TRUE, otherwise FALSE;
  • < (less than) – if the value of the variable on the left is less than the value of the variable on the right, the result is TRUE, otherwise FALSE;
  • < = (less than or equal to) – if the value of the variable on the left is less than or equal to the value of the variable on the right, the result is TRUE, otherwise FALSE;
  • > (greater than) – if the value of the variable on the left is greater than the value of the variable on the right, the result is TRUE, otherwise FALSE;
  • > = (greater than or equal to) – if the value of the variable on the left is greater than or equal to the value of the variable on the right, the result is TRUE, otherwise FALSE.

Examples:

if (x==y){ //Equal
  //Statement
}
 
if (x!=y){ //Not equal
  //Statement
}
 
if (x<y){ //Less than
  //Statement
}
 
if (x<=y){ //Less than or equal
  //statement
}
 
if (x>y){ //Greater than
  //Statement
}
 
if (x>=y){ //Greater than or equal
  //Statement
}

Boolean Operators

Three Boolean logical operators in the Arduino environment are the following:

  • ! (logical NOT) – reverses the logical state of the operand. If a condition is TRUE the logical NOT operator will turn it to FALSE and the other way around;
  • && (logical AND) – the result is TRUE when both operands on the left and on the right of the operator are TRUE. If even one of them is FALSE the result is FALSE;
  • || (logical OR) – the result is TRUE when at least one of the operands on the left and on the right of the operator is TRUE. If both of them are FALSE the result is FALSE.

Examples:

//Logical NOT
if (!a) { //The statement inside if will execute when the a is FALSE
  b = !a; //The reverse logical value of a is assigned to the variable b
}
 
//Logical AND
//The statement inside if will execute when the 
//Values both of the a and b are TRUE
if (a && b){  
  //Statement
}
 
//Logical OR
//The statement inside if will execute when at least one of the 
//a and b values is TRUE
if (a || b){  
  //Statement
}
Switch Case Statement

Switch statement similar like if statement controls the flow of program. The code inside switch is executed in various conditions. A switch statement compares the values of a variable to the specified values in the case statements. Allowed data types of the variable are int and char. The break keyword exits the switch statement.

Examples:

switch (x) { 
   case 0:  //Executes when the value of x is 0
   // statements
   break;   //Goes out of the switch statement
 
   case 1:  //Executes when the value of x is 1
   // statements
   break;   //Goes out of the switch statement
 
   default:  //Executes when none of the cases above is true
   // statements
   break;   //Goes out of the switch statement
}

Check Yourself

1. Which code part is the correct one?

  • if(value == 1) digitalWrite(13, HIGH)
  • if (value == 1); digitalWrite(13, HIGH)
  • if (value == 1) DigitalRead(13,1)

2. What is the output of the next code part?

int x = 0;
 
    switch(x)
    {
 
      case 1: cout << "One";
 
      case 0: cout << "Two";
 
      case 2: cout << "Hello, world!";
 
    }
 

3. In which cases switch structure should be used?

Looping

for

for is a cycle operator that allows specifying the number of times when the same statements will be executed. In this way, similar to the loop function it allows to control the program execution. Each time when all statements in the body of the cycle are executed is called the iteration. In this way, the cycle is one of the basic programming techniques that is used for all programs and automation in general.

The construction of a for cycle is the following:

for (initialization ; condition ; operation with the cycle variable) {
  //The body of the cycle
}

Three parts of the for construction is the following:

  • initialisation section usually initialises the value of the variable that will be used to iterate the cycle; this value can be 0 or any other value;
  • condition allows managing the number of cycle iterations; the statements in the body of the cycle are executed when the condition is TRUE;
  • operations with the cycle variable allows defining the number of cycle iterations.

The example of the for cycle:

for (int i = 0; i < 4; i = i + 1) 
{
    digitalWrite(13, HIGH); 		
    delay(1000);			
    digitalWrite(13, LOW);			
    delay(1000);	
}

On the initialization of the for cycle the variable i = 0 is defined. The condition states that the for cycle will be executed while the value of variable i will be less than 4 (i < 4). In the operation with the cycle variable it is increased by 1 each time when the cycle is repeated.

In this example above, the LED that is connected to the pin 13 of the Arduino board will turn on/off four times.

while

while cycle operator is similar to the for cycle operator that is described above, but it does not contain the cycle variable. Because of this, the while cycle allows to executed previously unknown number of iterations. The management of the cycle is realised using only condition that needs to be TRUE for the next operation to execute.

The construction of the while cycle is the following:

while (condition that is TRUE)
{
  //The body of the cycle
}

That way the while cycle can be used as a good instrument for the execution of a previously unpredictable program. For example, if it is necessary to wait until the signal from pin 2 reaches the defined voltage level – 100, the following code can be used:

int inputVariable = analogRead(2);
while (inputVariable < 100)
{
    digitalWrite(13, HIGH); 		
    delay(10);			
    digitalWrite(13, LOW);			
    delay(10);	
    inputVariable = analogRead(2);
}

In the cycle, the LED that is connected to the pin 13 of the Arduino board will be turned on/off while the signal will reach the specified level.

do...while

The do…while cycle works the same way like the while loop. The difference is that in the while cycle the condition is checked before entering the loop, but in the do…while cycle the condition is checked after execution of the statements in the loop and then if the condition is TRUE the loop repeats. As a result, the statements inside the cycle will execute at least once, even if the test condition is FALSE.

The construction of a do while cycle is the following:

do {
  //The body of the cycle
} while (condition that is TRUE);

If the same code is taken from the while loop example and used in the do…while cycle, the difference is that the code will execute at least once, even if the inputVariable value is more than or equal to 100. The example code:

int inputVariable = analogRead(2);
do {
    digitalWrite(13, HIGH); 		
    delay(10);			
    digitalWrite(13, LOW);			
    delay(10);	
    inputVariable = analogRead(2);
} while (inputVariable < 100);

Check Yourself

1. What is a kind of the loop, where the condition is checked after the loop body is executed?

2. How long will the operators in the body of the loop operate [while (x < 100)]?

3. What value will be for variable a after code executing?

 int a; for(a = 0; a < 10; a++) {} 

4. Which of the following operators are not loop(s) in Arduino IDE?

  • do…while,
  • while,
  • repeat until,
  • for.

Interrupts and Sub-Programs

Functions

Functions are the set of statements that are executed always when the function is called. Two functions that were mentioned before are already known – setup() and loop(). The programmer is usually trying to make several functions that contain all the statements and then to call them in the setup() or loop() functions.

The structure of the function is following:

type functionName(arguments) //A return type, name and arguments of the function
{
  //The body of a function – statements to execute
}

For the example, a function that periodically turns on and off the LED is created:

void exampleFunction() 
{
  digitalWrite(13, HIGH); //the LED is ON		
  delay(1000);			
  digitalWrite(13, LOW);  //the LED is OFF				
  delay(1000);	
}

In the example code can be seen that the return type of aexampleFunction function is void that means the function does not have the return type. This function also does not have any arguments because the brackets are empty.

This function should be called inside the loop() function in the following way:

void loop()
{
  exampleFunction(); //the call of the defined function inside loop()
}

The whole code in the Arduino environment looks like this:

void loop()
{
  exampleFunction(); //the call of the defined function inside loop()
}
 
void exampleFunction() 
{
  digitalWrite(13, HIGH); //the LED is ON		
  delay(1000);			
  digitalWrite(13, LOW);  //the LED is OFF				
  delay(1000);	
}

It can be seen that the function is defined outside the loop() or setup() functions.

When some specific result must be returned as a result of a function, then the function return type should be indicated, for example:

//the return type is "int"; arguments are still optional
int  sumOfTwoNumbers(int x, int y) 
{
    //the value next to the "return" should have the "int" type; 
    //this is what will be returned as a result.
    return (x+y); 
}

In the loop() this function would be called in a following way:

void loop()
{
  //the call of the defined function inside loop()
  int result = sumOfTwoNumbers(2, 3); 
}
Interrupts

Interrupt is a signal that stops the normal execution of a program in the processor, to be able to handle tasks with higher priority. These tasks usually are called Interrupt service routine (IRS) or interrupt handler. Interrupt signals can be generated from the external source, like a change of value on the pin and from the internal source, like a timer. When the interrupt signal is received, the processor stops executing the code and starts the IRS. After completing the IRS, the processor returns to the normal program execution state.

IRS should be as short as possible, and the return type of it is void. Some of normal Arduino functions do not work or behave differently in the IRS, for example, delay() function does not work in the IRS. Variables, used in the IRS must be volatile variables.

Interrupts are used to detect important real-time events, which occur during the normal code execution of the code, without continuously checking them, like pushing a button.

Different Arduino types have different external interrupt pin availability. In most Arduino boards pins, number 2 and 3 are used for interrupts.

To attach interrupt, the function attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) is called. This function has 3 parameters.

  1. pin – the number of a pin number where the interrupt signal generating device will be attached.
  2. ISR – the name of a function of interrupt service routine.
  3. mode – defines when interrupt signal is triggered. There are four basic mode values:
    • LOW – interrupt is triggered when the pin value is LOW,
    • HIGH – interrupt is triggered when the pin value is HIGH,
    • CHANGE – interrupt is triggered when the pin value is changed,
    • RISING – interrupt is triggered when the pin value is changed from LOW to HIGH.

The example program that uses interrupt:

volatile bool button =0; //A variable to save button state
 
void setup() {
  //Define LED pin
  pinMode(13,OUTPUT);       
  //Define button pin
  pinMode(2,INPUT_PULLUP);  
  //Attach interrupt to button pin
  attachInterrupt(digitalPinToInterrupt(2),ButtonIRS,FALLING); 
}
 
void ButtonIRS() {  //IRS function
  button =!button;
}
 
void loop() {
  digitalWrite (13,button);
}

Check Yourself

1. What are the built-in functions used for?

  • To reduce the size of the program.
  • To delete unnecessary functions.
  • To simplify the source file.
  • To increase the speed of the program.

2. Which of the following statements are true?

  • Built-in functions must return a value.
  • Built-in functions cannot return values.
  • The compiler can ignore the declaration of the built-in function.
  • Built-in functions cannot contain more than 10 lines of code.

3. Is it possible to guarantee that the declared built-in function is really built-in?

  • Guarantee is not possible, in each case it is different.
  • Can be confidently ensured that the function you have declared as built-in is really built-in.

Timing

Delay

The simplest solution to make functions work for a certain time is to use delay() [129] function. delay() function stops program execution for instructed time.

Blinking LED is a simple demonstration of delay functionality:

  digitalWrite(LED_BUILTIN, HIGH);   //Turn the LED on 
  delay(1000);                       //Stop program for a second
  digitalWrite(LED_BUILTIN, LOW);    //Turn the LED off 
  delay(1000);                       //Stop program for a second

Limitations that come with using delay(): most tasks stop, there can be no sensor reading, calculation, pin manipulation. Some tasks continue to work, like receiving serial transmissions and outputting set PWM values. Alternative of using delay is to use millis().

Millis

millis() [130] returns number in milliseconds since Arduino began running current program. The number will reset after approximately 50 days. millis() can be used to replace delay().

Here is an example code of blinking LED using millis(). Millis is used as a timer. Every new cycle time is calculated since the last LED state change if passed time is equal to or greater than the threshold value LED is switched:

//Unsigned long should be used to store time values
//Store value of current millis reading
unsigned long currentTime = 0; 
//Store value of time when last time the LED state was switched
unsigned long previousTime = 0; 
 
bool ledState = LOW; //Varible for setting LED state
 
const int stateChangeTime = 1000; //Time at which switch LED states
 
void setup() {
  pinMode (LED_BUILTIN, OUTPUT); //LED setup
}
 
void loop() {
  currentTime = millis(); //Read and store current time
 
  //Calculate passed time since last stateChange
  //If more time has passed than stateChangeTime, start state Change
  if (currentTime - previousTime >= stateChangeTime) { 
 
    previousTime = currentTime; //Store LED state change time
    ledState = !ledState; //Change LED state to oposite
    digitalWrite(LED_BUILTIN, ledState); //Write LED state to LED
  }
}
Protothread

Protothread is a mechanism for concurrent programming in embedded systems with limited resources. In Arduino, it can be used to achieve a periodical function call, like sensor reading, output state change, calculations etc. Most Arduino used microcontrollers have only one core; it can’t execute multiple functions simultaneously. By using millis() and loop, we can schedule a function call if appropriate conditions are met, thus avoiding unnecessary taking processors time.

In the example, code second LED is added, to demonstrate how to blink two LED’s concurrent with different frequencies. In the same manner, servo control, button reading and other functionality can be added.

//Unsigned long should be used to store time values
//Store value of current millis reading
unsigned long currentTime = 0; 
//Store value of time when last time LED1 state was switched
unsigned long previousTime1 = 0; 
//Store value of time when last time LED2 state was switched
unsigned long previousTime2 = 0; 
 
bool led1State = LOW; //Varible for setting LED1 state
bool led2State = LOW; //Varible for setting LED2 state
 
const int stateChangeTimeLed1 = 1000; //Time at which switch LED1 states
const int stateChangeTimeLed2 = 200; //Time at which switch LED2 states
 
void setup() {
  pinMode (LED_BUILTIN, OUTPUT); //LED1 setup
  pinMode (2,OUTPUT); //LED2 setup
}
 
void loop() {
  currentTime = millis(); //Read and store current time
 
  //Calculate passed time since the last stateChange
  //If more time has passed than stateChangeTime, start state change
 
  if (currentTime - previousTime1 >= stateChangeTimeLed1) { 
    previousTime1 = currentTime; //Store LED state change time
    led1State = !led1State; //Change LED1 state to oposite
    digitalWrite(LED_BUILTIN, led1State); //Write led1State to LED1
  }
    if (currentTime - previousTime2 >= stateChangeTimeLed2) { 
    previousTime2 = currentTime; //Store LED2 state change time
    led2State = !led2State; //Change led2state to oposite
    digitalWrite(2, led2State); //Write led2state to LED2
  }
}

There are dedicated libraries made by the Arduino community to implement protothreads [131].

Check Yourself

1. What are the drawbacks of using delay()?

  • Program execution stops.
  • Can’t read sensors during delay().
  • Simple code.

2. What is the difference between delay() and millis()?

3. Can Arduino run truly parallel programs?

4. How is concurrency achieved on Arduino?

Reading GPIOs, Outputting Data and Tracing

Digital I/O

The digital inputs and output of Arduino allow connecting different type of sensors and actuators to the board. Digital signals can take two values – HIGH(1) or LOW(0). These types of inputs and outputs are used in applications when the signal can have only two states.

pinMode()

The function pinMode() is essential to indicate whether the specified pin will behave like an input or an output. This function does not return any value. Usually, the mode of a pin is set in the setup() function of a program – only once on initialisation.

The syntax of a function is the following:

pinMode(pin, mode)

The parameter pin is the number of the pin.

The parameter mode can have three different values – INPUT, OUTPUT, INPUT_PULLUP depending on whether the pin will be used as an input or an output. The INPUT_PULLUP mode means that the pin will work as an input whose state will be inverted (set to the opposite). More about Pullup Resistors can be found in the Arduino homepage [132].

digitalWrite()

The function digitalWrite() writes a HIGH or LOW value to the pin. This function is used for digital pins, for example, to turn on/off LED. This function as well does not return any value.

The syntax of a function is the following:

digitalWrite(pin, value)

The parameter pin is the number of the pin. The parameter value can take values HIGH or LOW. If the mode of the pin is set to the OUTPUT, the HIGH sets voltage to +5 V and LOW – to 0 V.

It is also possible to use this function for pins that are set to have the INPUT mode. In this case, HIGH or LOW values enable or disable the internal pull-up resistor.

digitalRead()

The function digitalRead() works in the opposite direction than the function digitalWrite(). It reads the value of the pin that can be either HIGH or LOW and returns it.

The syntax of a function is the following:

digitalRead(pin)

The parameter pin is the number of the pin.

On the opposite as the functions viewed before, this one has the return type, and it can take a value of HIGH or LOW.

Analog I/O

The analogue inputs and outputs are used when the signal can take a range of values, unlike the digital signal that takes only two values (HIGH or LOW). For measuring the analogue signal, Arduino has built-in analogue-to-digital converter (ADC) that returns the digital value of the voltage level.

analogWrite()

The function analogWrite() is used to write an analog value (Pulse Width Modulation (PWM)) of the integer type as an output of the pin. The example of use is turning on/off the LED in various brightness levels or setting different speeds of the motors. The value that is written to the pin stays unchanged until the next value is written to the pin.

The syntax of a function is the following:

analogWrite(pin, value)

The parameter pin is the number of the pin.

The parameter value is the

This function does not have the return type.

analogRead()

The function analogRead() is used for analogue pins (A0, A1, A2, etc.) and it reads the value that is on the analogue pin.

The syntax of a function is the following:

analogRead(pin)

The parameter pin is the number of the pin whose value is read.

The return type of the function is the integer value between 0 and 1023. The reading of each analogue input takes around 100 ms that is 0.0001 s.

Check Yourself

1. To assign the Arduino pin operation mode, which function is using?

  • Function digitalWrite().
  • Function pinMode().
  • Directive #define.

2. What command for analogue input read is used?

3. The digital output on Arduino works as a power source with voltage?

  • 5 V.
  • 12 V.
  • 3.3 V.

Espressif SoC Overview

Arduino, along with a vast amount of peripheral boards, lacks integration of the networking capabilities in one SoC. Espressif ESP series was the natural answer for this disadvantage as their ESP 8266 with integrated WiFi, introduced in 2014, is widely recognised as a turning point for the IoT market, delivering de-facto fully functional IoT chip, providing high performance and low power to the end users and developers. ESP 32 launched in 2016 brought even more disrupting effect to the IoT ecosystems, introducing additional Bluetooth interface to the above. By the January 2018 company announced they delivered to the market 100 000 000 [133], thus constituting a de-facto standard for the IoT market devices.

Following chapters provide an overview of the networking programming with the use of ESP SoCs.

Please note, interfacing with sensors and actuators has been already covered in the chapter related to Arduino: “ 4.3. Arduino and Arduino 101 (Intel Curie)”.
The major difference is that ESP SoCs (both 8266 and 32) use 3.3 V logic, while most (but not all!) Arduinos use 5 V logic. This can be easily handled using one or bi-directional voltage converters/adapters. Additionally, many ESP boards and development kits offer double power source, including 5 V, even if the device itself still operates on 3.3 V. Yet programming principals used to be the same while using digital protocols and GPIOs.

Espressif SoC

Espressif System-on-chip (SoC) devices are low-cost microcontrollers with full TCP/IP stack capability produced by Shanghai-based Chinese manufacturer, Espressif Systems [134]. The two most popular series of these microcontrollers are:

  • ESP8266,
  • ESP32.

ESP 8266 General Information

The ESP8266 is a low-cost system on chip (SoC) microcontroller with WiFi and full TCP/IP stack capability [135]. The chip first came to the market with the ESP-01 module, made by a third-party manufacturer, Ai-Thinker. This small module allows microcontrollers to connect to a WiFi network and make simple TCP/IP connections using Hayes-style AT commands. The low price and the fact that there were very few external components on the module, which suggested that it could eventually be very inexpensive in volume, attracted many users to explore it. The ESP8285 is an ESP8266 with 1 MiB of built-in flash, allowing for single-chip devices capable of connecting to WiFi. The successor to these microcontroller chips is the ESP32. For now, the ESP8366 family includes the following chip:

  • ESP8266EX (Figure 224).
Figure 224: ESP8266EX chip.
Esp8266 Architecture Overview

Main standard features of the ESP8266EX are:

Processor

  • Main processor: L106 32-bit RISC microprocessor core based on the Tensilica Xtensa Diamond Standard 106Micro running at 80 MHz. Both the CPU and flash clock speeds can be doubled by overclocking on some devices. CPU can be run at 160 MHz, and flash can be sped up from 40 MHz to 80 MHz. Success varies chip to chip.

Memory

  • 32 KiB instruction RAM.
  • 32 KiB instruction cache RAM.
  • 80 KiB user data RAM.
  • 16 KiB ETS system data RAM.
  • External QSPI flash: up to 16 MiB is supported (512 KiB to 4 MiB typically included).

Interfaces

  • IEEE 802.11 b/g/n WiFi .
  • Integrated TR switch, balun, LNA, power amplifier and matching network WEP or WPA/WPA2 authentication, or open networks.
  • 16 GPIO pins.
  • SPI.
  • I²C (software implementation).
  • I²S interfaces with DMA (sharing pins with GPIO).
  • UART on dedicated pins, plus a transmit-only UART can be enabled on GPIO2.
  • 10-bit ADC (successive approximation ADC).

Figure 225 shows function block of ESP8266 chip diagram [136].

Figure 225: ESP8266 chip main functions.
ESP8266 Modules

There are many ESP8266 based modules on the market [137]. These modules combine ESP8266EX microcontroller and additional components mounted on PCB.

The most popular are these produced by AI-Thinker and remain the most widely available [138]:

  • ESP-01 (512 KiB Flash),
  • ESP-01S (1 MiB Flash),
  • ESP-12 (FCC and CE approved),
  • ESP-12E,
  • ESP-12F (4 MiB Flash, FCC and CE approved).

Popular modules from other manufacturers:

  • Sparkfun ESP8266 Thing,
  • Wemos D1 mini, D1 mini Pro [139].

The Espressif company also produces ready-made modules using the aforementioned chip. This is the series of ESP8266-based modules made by Espressif (Table 20).

Table 20: Espressif ESP8266 modules
NameActive pinsLEDsAntennaShieldedDimensions (mm)Notes
ESP-WROOM-02[140]18NoPCB traceYes18 × 20FCC ID 2AC7Z-ESPWROOM02
ESP-WROOM-02D[141]18NoPCB traceYes18 × 20FCC ID 2AC7Z-ESPWROOM02D. Revision of ESP-WROOM-02 compatible with both 150-mil and 208-mil flash memory chips
ESP-WROOM-02U[142]18NoU.FL socketYes18 × 20Differs from ESP-WROOM-02D in that includes an U.FL compatible antenna socket connector
ESP-WROOM-S2[143]20NoPCB traceYes16 × 23FCC ID 2AC7Z-ESPWROOMS2

The most widely used and chipest ESP-01 is presented on (Figure 226) and its pinout on (Figure 227).

Figure 226: ESP-01.
Figure 227: ESP-01 pinout.

Module ESP12F with pinout is presented on (Figure 228) and its pinout on (Figure 229).

Figure 228: ESP-12F.
Figure 229: ESP-12F pinout.

Among the other modules, it is worth to be interested in WEMOS modules [144] (Figure 230, Figure 231). The WEMOS company offers dedicated sensor modules and inputs/outputs compatible with the processor modules. They are called WEMOS shields (Figure 232).

Figure 230: Wemos D1 mini with pinout.
Figure 231: Wemos D1 Pro.
Figure 232: Wemos I/O shields.

ESP32 NodeMCU pins (Figure 233).

Figure 233: NodeMCU v.2 pins.

ESP32 General Information

ESP32 is a low-cost, low-power system on a chip (SoC) series microcontrollers with WiFi & dual-mode Bluetooth capabilities [145]. ESP32 SoC is highly integrated with built-in antenna switches, power amplifier, low-noise receive amplifier, filters, and power management modules. Inside all family there is a single-core or dual-core Tensilica Xtensa LX6 microprocessor with a clock rate of up to 240 MHz. ESP32 is designed for mobile, wearable electronics, and Internet-of-Things (IoT) applications. It features all the state-of-the-art characteristics of low-power chips, including fine-grained clock gating, multiple power modes, and dynamic power scaling. For now the ESP32 family includes the following chips:

  • ESP32-D0WDQ6 (Figure 234),
  • ESP32-D0WD (Figure 235),
  • ESP32-D2WD (Figure 236),
  • ESP32-S0WD (Figure 237),
  • ESP32-PICO-D4 – SiP (system in package) (Figure 238) – additionally contains crystal oscillator, 4MiB flash memory, filter capacitors and RF matching links.
Figure 234: ESP32-D0WDQ6.
Figure 235: ESP32-D0WD.
Figure 236: ESP32-D2WD.
Figure 237: ESP32-S0WD.
Figure 238: ESP32-PICO-D4.
ESP32 Architecture Overview

(Figure ##REF:esp32_functions##) shows function block diagramm of ESP32 chip. Main common features of the ESP32 are: [146] [147].

Processors

  • Main processor: Tensilica Xtensa 32-bit LX6 microprocessor.
    • Cores: 2 or 1 (depending on variation). (All chips in the ESP32 series are dual-core except for ESP32-S0WD, which is single-core.)
    • Internal 8 Mhz oscillator with calibration.
    • External 2 MHz to 60 MHz crystal oscillator (40 MHz only for WiFi/BT functionality).
    • External 32 kHz crystal oscillator for RTC with calibration.
    • Clock frequency: up to 240 MHz.
    • Performance: up to 600 DMIPS.
  • Ultra low power co-processor: allows you to do ADC conversions, I2C connecting, computation, and level thresholds while in a deep sleep.

Wireless connectivity

  • WiFi: 802.11 b/g/n/e/i (802.11n @ 2.4 GHz up to 150 Mbit/s) with simultaneous Infrastructure BSS Station mode/SoftApp mode/Promiscuous mode.
  • Bluetooth: v4.2 BR/EDR and Bluetooth Low Energy (BLE) with multi-connections Bt and BLE and simultaneous advertising and scanning capability.

Memory: Internal memory

  • ROM: 448 KiB (for booting and core functions).
  • SRAM: 520 KiB (for data and instruction).
  • RTC fast SRAM: 8 KiB (for data storage and main CPU during RTC Boot from the deep-sleep mode).
  • RTC slow SRAM: 8 KiB (for co-processor accessing during deep-sleep mode).
  • eFuse: 1 Kibit (of which 256 bits are used for the system (MAC address and chip configuration) and the remaining 768 bits are reserved for customer applications, including Flash-Encryption and Chip-ID).
  • Embedded flash (flash connected internally via IO16, IO17, SD_CMD, SD_CLK, SD_DATA_0 and SD_DATA_1 on ESP32-D2WD and ESP32-PICO-D4):
    • 0 MiB (ESP32-D0WDQ6, ESP32-D0WD, and ESP32-S0WD chips),
    • 2 MiB (ESP32-D2WD chip),
    • 4 MiB (ESP32-PICO-D4 SiP module).

External Flash & SRAM

  • ESP32 supports up to four 16 MiB external QSPI flashes and SRAMs with hardware encryption based on AES to protect developers' programs and data. ESP32 can access the external QSPI flash and SRAM through high-speed caches.
  • Up to 16 MiB of external flash are memory-mapped onto the CPU code space, supporting 8-bit, 16-bit and 32-bit access. Code execution is supported.
  • Up to 8 MiB of external flash/SRAM memory is mapped onto the CPU data space, supporting 8-bit, 16-bit and 32-bit access. Data-read is supported on the flash and SRAM. Data-write is supported on the SRAM.

ESP32 chips with embedded flash do not support the address mapping between external flash and peripherals.

Peripheral Input/Output

  • Rich peripheral interface with DMA that includes capacitive touch (10× touch sensors).
  • 12-bit ADCs (analog-to-digital converter) up to 18 channels.
  • 2 × 8 bit DACs (digital-to-analog converter).
  • 2 × I²C (Inter-Integrated Circuit.
  • 3x UART (universal asynchronous receiver/transmitter).
  • CAN 2.0 (Controller Area Network).
  • 4 × SPI (Serial Peripheral Interface).
  • 2 × I²S (Integrated Inter-IC Sound).
  • RMII (Reduced Media-Independent Interface).
  • Motor PWM (pulse width modulation).
  • LED PWM up to 16 channels.
  • Hall sensor.
  • Internal temperature sensor.

Security

  • Secure boot.
  • Flash encryption.
  • IEEE 802.11 standard security features all supported, including WFA, WPA/WPA2 and WAPI.
  • 1024-bit OTP, up to 768-bit for customers.
  • Cryptographic hardware acceleration:
    • AES,
    • SHA-2,
    • RSA,
    • elliptic curve cryptography (ECC),
    • random number generator (RNG).
Figure 239: ESP32 Function block diagram.
ESP32 Modules

The company also produces ready-made modules using the aforementioned processors. These modules combines ESP32 microcontroller and additional components mounted on PCB with EM shield:

  • ESP32-WROOM-32 with 4 MiB flash memory, and antenna on PCB (Figure 240);
  • ESP32-WROOM-U with 4 MiB flash memory and u.fl antenna conector (Figure 241);
  • ESP32-WROVER – with 4 MiB flash memory, 4 MiB pseudo static RAM and antenna on PCB (Figure 242);
  • ESP32-WROVER-I – as ESP32-WROVER with additional u.fl antenna connector (Figure 243).
Figure 240: ESP32-WROOM-32.
Figure 241: ESP32-WROOM-U.
Figure 242: ESP32-WROVER.
Figure 243: ESP32-WROVER-I
ESP32 Development Kits

To accelerate the design of circuits, developers can use specially prepared sets with ESP32 which are ready to use. The original Espressif best known small development boards are:

  • ESP32-DevkitC (Figure 244);
  • ESP32-PICO-KIT-V4 (Figure 245).
Figure 244: ESP-32-DevkitC
Figure 245: ESP-32-PICO-KIT-V4.
General Purpose Input-Output (GPIO) Connector

Each ESP32 is equipped with standard 38/40-pis male connector containing universal GPIO ports, VCC 3.3/5 V, GND, CLK, I2C/SPI buses pins which developers can use to connect their external sensors, switches and other controlled devices to the ESP32 board and then program their behaviour within the code loaded to the board.

  • ESP32-DevkitC v2 pins (Figure 246).
Figure 246: ESP32-DevkitC pins.
  • ESP32-PICO D4 pins (Figure 247).
Figure 247: ESP32-Pico D4 pins.
  • ESP32 Wemos Pro pins (Figure 248).
Figure 248: ESP32-Pico D4 pins.

Espressif SoC Networking

Using Espressif SoC devices in Arduino platform we can use all the previously described Arduino examples for sensors and actuators. But the most interesting are are those that use wireless networking functions. Both Espressif chip ESP32 and ESP8266 families can use similar network modes. As a WiFi device Espressif SoC can work in different networking modes (applies to medial layers 1–3 ISO-OSI model):

  • as WiFi client connected to WiFi router (Figure 249),
Figure 249: ESP client mode.
  • as independent WiFi access point (Figure 250),
Figure 250: ESP AP mode.
  • as a repeater with devices connected to ESP and ESP connected to external router (Figure 251),
Figure 251: ESP dual mode.
  • as client and server in mesh network (Figure 252).
Figure 252: ESP mesh networking.

ESP32 can also use Bluetooth networking in the same configuration as WiFi.

ESP Programming Fundamentals

The following sub-chapters cover programming fundamentals for ESP chips in C/C++, which complies with the most C/C++ notations and have some specific notations. Please note, this is a particular implementation of the programming, compatible with Arduino standards and Arduino(C) IDE. Following chapters present particular aspects of the network programming with ESP chips. Other structures (program flow control, GPIO, etc.) are shown in the Arduino section of this manual and apply straightforwardly to the ESP programming.

This manual refers to the particular version of the software (here Arduino IDE, ESP 8266 and ESP32 and related toolkits) available at the moment of writing of this book, thus accessing particular features may change over time along with the evolution of the platform. Please refer attached documentation (if any) and browse Internet resources to find lates guidances on how to configure development platform, when in doubt. In particular, following links present up-to-date help on “how to start” guides with ESP 8266 and ESP32 platforms and Arduino IDE. Please refer here, when guides present in following sub-chapters are outdated:

Further reading:

Setting Up Development Environment for ESP SoC Programming

Following subchapters present guides for setting up the environment to enable you to use Arduino(C) IDE to develop solutions for ESP8266 and ESP32. Installation methodology varies much as ESP8266 is already integrated with so-called Board Manager in Ardiono(C) iDE, while ESP32 is somehow external to the Boards Manager and requires some steps to be done, to obtain fully integrated environment. Some of the steps vary in details among various operating systems you use (Windows/Mac/Linux), see details, but in any case, there is a common idea, what to do in following steps. Once it is done, programming of both ESP8266 and ESP32 does not differ much from programming, i.e. Arduino Uno, natively supported by Arduino(C) IDE.

There do exists other solutions i.e. PlatformIO [150] + Visual Studio Code IDE [151] or Atom IDE [152], MicroPython [153], NodeMCU [154](for ESP8266), etc. Some of them provide advanced features, like debugging (usually requires extra hardware, i.e. JTAG), multithreaded and parallel programming, etc. A detailed description of those solutions is out of the scope of this manual, however. Please refer to the links provided below to obtain detailed steps, how to configure other development environments.
Download and Run Arduino IDE

We consider here software that is natively installed as a binary package on your operating system, not a WEB version of the IDE. To obtain the latest version of the binary package, visit the website [155], as on figure 253 or install it via integrated software management application (Windows App Store, iTunes, Linux Application Store, etc.).

Figure 253: Arduino(C) IDE downloads web page.
Linux distros usually offer an outdated version of the Arduino(C) IDE through its repositories; thus we suggest to install one through downloading package directly from the Arduino IDE website.
There are two versions of the Arduino(C) IDE for Windows: one you can download from their website and the other is available via Microsoft Windows Application Store. If you experience compilation problems with Windows Store version, please use the installer from their website and install it manually.
Configuring Arduino IDE for ESP8266 Development

Assuming you've purchased any of the ESP8266 development boards, it is essential to write the first program. In any case, you'll need a way to write, compile and upload your code to the ESP8266 SoC. Here Arduino(C) IDE comes handy, however before you start, you need to let the Arduino(C) IDE knows, how to compile and communicate with your ESP8266 chip. Below there is a short manual, presenting how to install development extension to the Arduino(C) IDE through the Boards Manager. Note, other solutions (i.e. manual installation via Github pull) is also possible, but we do not consider this option here. ESP8266 core for Arduino(C) IDE is maintained by the community, and the latest version is available here (along with up-to-date installation guide) [156].

Install ESP8266 Boards via Board Manager

Start Arduino(C) IDE (Figure 254):

Figure 254: Arduino IDE.

Enter application menu File/Preferences:

Figure 255: System menu and preferences.

and then go to the Additional Boards Manager URLs:, enter following URL and accept changes (press OK) (Figure 256):

http://arduino.esp8266.com/stable/package_esp8266com_index.json
Figure 256: Preferences dialog.

Once finished, you need to tell the Board Manager, which definitions and tools to download. Open Tools/Board:/Boards Manager (Figure 257):

Figure 257: Board Manager menu.

and filter all boards entering “ESP8266” in the Search area, as on figure 258, then click Install:

Figure 258: Board installation dialog.

Note – installation downloads number of resources over Internet connection and may take some time.

Configure Project to Compile for ESP8266

Once you're done with Arduino(C) IDE configuration now it is time to start programming. The process above installs a number of board definitions. First, depending on the development kit you own, select the appropriate type of the development board in the Board Manager menu, i.e. WeMos D1 R2 & mini (Figure 259):

Figure 259: Selecting ESP8266 development board.

Communication Between Development Machine and ESP SoC

Most of the ESP development boards come with integrated programming interface via serial to USB converter (usually CH340, CP210x, FDTI, Prolific, etc.) that you connect to the development machine using the USB cable. Connector standards vary, but nowadays the most popular seems to be Micro USB connector. There is also possible to upload your compilation using wireless transmission (OTA – Over The Air), or dedicated programming device, but we do not consider it here as too complicated to implement (requires special firmware) and also insecure. You need to select correct device interface – it differs, depending on the operating system you use, see details below how to identify it in your computer.




Windows

Look into the Device Manager to identify COM port, your board is represented by, in the Windows OS (Figure 260, here COM4):

Figure 260: Windows COM port identification.

Linux

In case of the Linux distributions, run in the terminal:

lsusb

and

ls /dev/ttyUSB*

or

ls /dev/ttyACM*

to identify your board (Figure 261):

Figure 261: Linux serial devices identification.

Mac OS

The similar way to Linux distros, look for the devices using:

ls /dev/tty.usbmodem*

or

ls /dev/tty.usbserial*

to get a list of connected devices providing serial port feature (Figure 262):

Figure 262: Mac OS serial devices identification.

Configure Details for the SoC

Now select appropriate device in the Arduino IDE (Figure 263) and select communication speed (Figure 264). Eventually, change the other parameters. Details on the flash size, its organisation, communication speed and frequencies should be provided by your hardware vendor. If you experience errors during programming or programming hangs, try to reduce programming speed, as your computer may be not quick enough to deliver data stream over USB. Devices usually present their maximum programming (flashing) speed, and it is common they tolerate lower speeds.

Figure 263: Selecting serial port.
Figure 264: Selecting flashing speed.

Troubleshooting Access Denied Error

In case of the Linux and Mac, depending on the security context you run your Arduino(C) IDE, you may experience “Access Denied” error. There are several workarounds to this problem, starting from running Arduino IDE with sudo credentials (not recommended) through creating udev rules (advanced) to simplest – providing credentials on-demand and ad-hoc. The disadvantage of this method is you must usually run those command every time you reboot OS or reconnect your board, yet is simplest to handle (Figure 265 and 266):

sudo usermod -a -G dialout $USER
sudo chmod a+rw /dev/ttyUSB0
Figure 265: Linux serial port access denied troubleshooting, part 1.
Figure 266: Linux serial port access denied troubleshooting, part 2.

A need to run one or two of the commands above strongly depends on the operating system configuration.

In case of the Windows OS, marking application to “Run as Administrator” may help to remove permission related errors (Figure 267):

Figure 267: Runing Arduino IDE as Administrator in Windows OS.
Preparing ESP32 Development Environment With Arduino IDE

ESP32 SoC is a continuation and extension to the ESP8266 SOCs. At the moment of writing this manual, installation of the ESP32 development environment is not supported via integrated Board Manager of the Arduino(C) IDE, as presented above in the ESP8266 section. Uploading your binary to the ESP32 chip via USB to serial converter requires Python as the ESP32 flashing tool is written in the form of the Python script. In case you're aware of what Python programming language is and how to install it on your machine, please refer to the Python website [157] and install Python before continuing.

Installing ESP32 Core for Arduino IDE

Depending on the operating system you use, there is necessary to perform some steps to obtain a fully functional ESP32 development environment for Arduino(C) IDE. ESP32 core for Arduino is maintained by the community, and the latest version is available here (along with up-to-date installation guide) [158]. Steps tend to have the same meaning, but tools to obtain the result are different for different operating systems.

Linux
A guide for the most popular distros (Ubuntu/Debian) is presented below.

Modify user credentials
This step is optional, see related ESP8266 section. Run following command in the terminal to add a user to the dialout group:

sudo usermod -a -G dialout $USER

Download and install git client
Run following command in the terminal:

sudo apt-get install git

Download and install python tools and packages
Run following commands in the terminal:

wget https://bootstrap.pypa.io/get-pip.py && \
sudo python get-pip.py && \
sudo pip install pyserial

Create destination folders and clone repository
Assuming your Arduino IDE is installed in your home directory, (~/Arduino), run the folowing code in terminal:

mkdir -p ~/Arduino/hardware/espressif && \
cd ~/Arduino/hardware/espressif && \
git clone https://github.com/espressif/arduino-esp32.git esp32 && \
cd esp32

Pull depending modules and tools
Run following commands in the terminal:

git submodule update --init --recursive && \
cd tools && \
python2 get.py

Then start Arduino IDE.

Windows
Installing ESP32 core for Arduino requires Git client, both GUI and bash as well as command line operations.

Install Git and clone repository
You can download and install Git [159]. Once installed, choose “Clone Existing Repository” (Figure 268):

Figure 268: Git GUI for Windows.

Use ESP32 core for Arduino repository address as source:

https://github.com/espressif/arduino-esp32.git

The destination folder depends on where you've installed Arduino IDE and your Windows user name. The common location is:

C:/Users/[YOUR_USER_NAME]/Documents/Arduino/hardware/espressif/esp32

note, you do not need to create this folder manually. If you install fresh copy of the Arduino IDE, perhaps there will be no hardware subfolder, but Git GUI will create remaining path for you. Once entered Source and Destination, click “Clone” (Figure 269). Cloning may take a while.

Figure 269: Configure Git GUI for cloning.

Pull depending modules
Use Git Bash command line (not a Windows command line!) to install dependencies (Figure 270). Change directory to the esp32 git local copy then run submodules install:

cd Documents/Arduino/hardware/espressif/esp32/
git submodule update --init --recursive
Figure 270: Updating sub modules using Git Bash.

Download ESP32 tools
Open Windows command line (not a Git Bash command line!), navigate to the tools folder (Figure 271):

cd C:/Users/[YOUR_USER_NAME]/Documents/Arduino/hardware/espressif/esp32/tools

then run:

get.exe
Figure 271: Get ESP32 tools.

Mac OS
Instruction for the Mac is similar to this for Linux. They require terminal to issue a set of commands to install ESP32 development kit and tools.

Create destination folders and clone source

mkdir -p ~/Documents/Arduino/hardware/espressif && \
cd ~/Documents/Arduino/hardware/espressif && \
git clone https://github.com/espressif/arduino-esp32.git esp32 && \
cd esp32

Pull depending modules and tools
Run following commands in the terminal:

git submodule update --init --recursive && \
cd tools && \
python get.py

Then start Arduino IDE. Troubleshooting
If you get the following error during installation:

xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), 
missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun

then install command line developer tools using:

xcode-select --install

Configure Project to Compile for ESP32

Once ESP32 platform is installed, start Arduino IDE and you should see new board definitions, similar to those presented on the figure 272:

Figure 272: ESP32 boards in Arduino IDE.

Refer to your board vendor for information about compatible configurations and setting up upload parameters. Detailed description and information on selecting communication port and upload speed is presented in the ESP8266 section, above.

ESP AT Networking

Flashing AT Firmware

To use the ESP8266 chip as a modem (Figure 273) we must first load the appropriate AT-command firmware.

Figure 273: ESP8266 as a modem.
Download Software
  1. Download the latest ESP Flash Download Tool (v3.6.4 at the time of writing) from [160].
  2. Download the latest AT release from [161] or [162].
Flashing Procedure
  • Detect ESP8266 module parameters. Start ESP Flash Download Tool (“ESPFlashDownloadTool_v3.6.4”), set the COM port corresponding to your programmer, then click the START button in order to detect the specs of the board. After detection, you should see something like this (Figure 274):
Figure 274: Programming ESP8266 – detected parameters.
  • Gather information. Make a note of the flash memory size. In this example, we have a 32 Mbit flash.
  • Load the correct size of combined AT binary firmware file (“.bin”) and set the offset as 0 × 0, you should see something like this (Figure 275):
Figure 275: Programming ESP8266 – setting proper image file.
  • Then, click the START button and wait until the flashing process is over.
Reflashing Procedure

If necessary, to restore the original firmware:

  • Detect ESP8266 module parameters. Start ESP Flash Download Tool (“ESPFlashDownloadTool_v3.6.4”), set the COM port corresponding to your programmer, then click the START button in order to detect the specs of the board. After detection, you should see something like this (Figure 276):
Figure 276: Programming ESP8266 – detected parameters.
  • From the downloaded AT firmware folder, open the “readme.txt” file containing the information for flashing the firmware. Inside the file, there should be a “BOOT MODE” section, as follows:
# BOOT MODE
## download
### Flash size 8Mbit: 512KB+512KB
    boot_v1.2+.bin              0x00000
    user1.1024.new.2.bin        0x01000
    esp_init_data_default.bin   0xfc000 (optional)
    blank.bin                   0x7e000 & 0xfe000

### Flash size 16Mbit: 512KB+512KB
    boot_v1.5.bin              0x00000
    user1.1024.new.2.bin        0x01000
    esp_init_data_default.bin   0x1fc000 (optional)
    blank.bin                   0x7e000 & 0x1fe000

### Flash size 16Mbit-C1: 1024KB+1024KB
    boot_v1.2+.bin              0x00000
    user1.2048.new.5.bin        0x01000
    esp_init_data_default.bin   0x1fc000 (optional)
    blank.bin                   0xfe000 & 0x1fe000

### Flash size 32Mbit: 512KB+512KB
    boot_v1.2+.bin              0x00000
    user1.1024.new.2.bin        0x01000
    esp_init_data_default.bin   0x3fc000 (optional)
    blank.bin                   0x7e000 & 0x3fe000

### Flash size 32Mbit-C1: 1024KB+1024KB
    boot_v1.2+.bin              0x00000
    user1.2048.new.5.bin        0x01000
    esp_init_data_default.bin   0x3fc000 (optional)
    blank.bin                   0xfe000 & 0x3fe000
  • Indicate – correct for your ESP8266 flash size – firmware files & addresses. The firmware is broken down into several files. They need to be provided to the ESP Flash Download Tool, together with the corresponding addresses found in the readme.txt file above. For our ESP8266 example it should look like this (Figure 277)
Figure 277: Programming ESP8266 – reflashing settings.
  • Then, click the START button and wait until the flashing process is over.

Basic ESP8266 Networking

After uploading AT firmware and connecting module to PC, we can use ESP8266 as a modem with simple AT commands.

We can connect ESP8266 to PC with TTL-Serial-to-USB adapter, or we can use any microcontroller with a serial interface. The default baud rate settings are 115200,N,8,1. Next from any terminal type command:

AT

and press enter. If you get OK, the ESP8266 module is ready to use. Let’s try out some other commands. For example, let’s figure out exactly what firmware version we’re dealing with. To do that, we’ll use the following command:

AT+GMR

As a Wifi device ESP8266 can connect to the network in such modes:

  • mode 1 – client mode – the ESP8266 connecting to an existing wireless network,
  • mode 2 – access point mode (AP) – other wireless network devices can be connected to the ESP8266,
  • mode 3 – dual mode (router) – the ESP8266 act as an access point and connect at the same time to an existing wireless network.

By default, the ESP8266’s stock firmware is set to AP mode. If you’d like to confirm that, send the following command:

AT+CWMODE?

You should get this response: +CWMODE:2, where 2 corresponds to AP mode. To switch ESP8266 to client device mode, we use the following command:

AT+CWMODE=1

Now we can scan the airwaves for all WiFi access points in range. To do that, we send:

AT+CWLAP

Then the ESP8266 will return a list of all the access points in range. In with each line will be item consisting of the security level of the access point, the network name, the signal strength, MAC address, and wireless channel used. Possible security levels of the access point <0–4> mean:

  • 0 – open,
  • 1 – WEP,
  • 2 – WPA_PSK,
  • 3 – WPA2_PSK,
  • 4 – WPA_WPA2_PSK.

Now we can connect to the available access point using proper “ssid_name” and “correct_password” with the command:

AT+CWJAP="ssid_name","corect_password"

If everything is OK, the ESP8266 will answer:

WIFI CONNECTED
WIFI GOT IP
OK

It means that ESP8266 is connected to the chosen AP and got a proper IP address. To check what the assigned address is we send the command:

AT+CIFSR

To set up ESp8266 to behave both as a WiFi client as well as a WiFi Access point.

AT+CWMODE=3

ESP Network Layers

Programming networking services with ESP requires a connection on the networking layer between parties, mostly TCP.
ESP SoC can act as Access Point (AP): a device you connect to, like you connect a notebook to the Internet router, and as a client: ESP then behaves like any wifi enabled device, i.e. tablet or mobile phone, connecting to the Internet infrastructure. Interestingly, ESP 8366 SoC can act simultaneously in both modes at once, even, if it has only one WiFi interface!
Below there is sample code, how to implement both modes, using ESP libraries that came during installation of the development environment for Arduino IDE.
The third example shows how to send and receive a UDP packet while in client mode. It is the full solution to connect ESP to the NTP (Network Time Protocol) server to obtain current date and time from the Internet.
Last examples show, how to make a handy WiFi scanner showing available networks nearby.

ESP8266 AP (Access Point) Mode

This sketch based on standard example demonstrates how to program ESP8266 in AP mode:

#include <ESP8266WiFi.h>
#include <WiFiClient.h> 
#include <ESP8266WebServer.h>
 
/* Set these variables to your desired credentials. */
const char *ssid = "APmode";
const char *password = "password";
 
ESP8266WebServer server(80);
 
void hRoot() {
	server.send(200, "text/html", "<h1>You are connected</h1>");
}
 
/* Initialization */
void setup() {
	delay(1500);
	/* You can remove the password parameter if you want the AP to be open. */
	WiFi.softAP(ssid, password);
 
	IPAddress myIP = WiFi.softAPIP();
 
	server.on("/", hRoot);
	server.begin();
}
 
void loop() {
	server.handleClient();
}
ESP8266 Client Mode

This sketch (standard example) demonstrates how to program ESP8266 in client mode:

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
 
ESP8266WiFiMulti WiFiMulti;
 
void setup() {
    delay(1000);
 
    // We start by connecting to a WiFi network
    WiFi.mode(WIFI_STA);
    WiFiMulti.addAP("SSID", "password");
 
    while(WiFiMulti.run() != WL_CONNECTED) {
        delay(500);
    }
    delay(500);
}
 
 
void loop() {
    const uint16_t port = 80;
    const char * host = "192.168.1.1"; // ip or dns
 
    // Use WiFiClient class to create TCP connections
    WiFiClient client;
 
    if (!client.connect(host, port)) {
        delay(5000);
        return;
    }
 
    // This will send the request to the server
    client.println("Send this data to server");
 
    //read back one line from server
    String line = client.readStringUntil('\r');
    Serial.println(line);
 
    Serial.println("closing connection");
    client.stop();
 
    Serial.println("wait 5 sec...");
    delay(5000);
}
ESP8266 and UDP

This sketch (based on standard example) demonstrates how to program ESP8266 as NTP client using UDP packets (send and receive):

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
 
 
char ssid[] = "**************";  //  your network SSID (name)
char pass[] = "**************";  // your network password
 
unsigned int localPort = 2390;      // local port to listen for UDP packets
 
// NTP servers
IPAddress ntpServerIP; // 0.pl.pool.ntp.org NTP server address
const char* ntpServerName[] = 
 {"0.pl.pool.ntp.org","1.pl.pool.ntp.org","2.pl.pool.ntp.org","3.pl.pool.ntp.org"};
 
const int timeZone = 1;  //Central European Time
int servernbr=0;
 
// NTP time stamp is in the first 48 bytes of the message
const int NTP_PACKET_SIZE = 48; 
 
//buffer to hold incoming and outgoing packets
byte packetBuffer[ NTP_PACKET_SIZE]; 
 
// A UDP instance to let us send and receive packets over UDP
WiFiUDP udp;
 
void setup()
{
  Serial.begin(115200);
  Serial.println();
 
  Serial.print("Connecting to ");
  Serial.println(ssid);
 
//  WiFi.persistent(false);
  WiFi.mode(WIFI_OFF);
  delay(2000);
 
// We start by connecting to a WiFi network
  WiFi.mode(WIFI_STA);
  delay(3000); 
  WiFi.begin(ssid, pass); 
 
  while (WiFi.status() != WL_CONNECTED) {   
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("");
 
  Serial.println("WiFi connected");
  Serial.println("DHCP assigned IP address: ");
  Serial.println(WiFi.localIP());
 
  Serial.println("Starting UDP");
  udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(udp.localPort());
 
  // first ntp server
  servernbr = 0;
}
 
void loop()
{
  //get a random server from the pool
 
  WiFi.hostByName(ntpServerName[servernbr], ntpServerIP); 
  Serial.print(ntpServerName[servernbr]); 
  Serial.print(":");
  Serial.println(ntpServerIP);
 
  sendNTPpacket(ntpServerIP); // send an NTP packet to a time server
  // wait to see if a reply is available
  delay(1000);
 
  int cb = udp.parsePacket();
  if (!cb) {
    Serial.println("no packet yet");
    if ( servernbr = 5 ) {
      servernbr =0;
    }
    else {
      servernbr++;
    }
  }
  else {
    Serial.print("packet received, length=");
    Serial.println(cb);
    // We've received a packet, read the data from it
    udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
 
    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:
 
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("Seconds since Jan 1 1900 = " );
    Serial.println(secsSince1900);
 
    // now convert NTP time into everyday time:
    Serial.print("Unix time = ");
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;
    // print Unix time:
    Serial.println(epoch);
 
 
    // print the hour, minute and second:
    // UTC is the time at Greenwich Meridian (GMT)
    Serial.print("The UTC time is ");       
    // print the hour (86400 equals secs per day)
    Serial.print((epoch  % 86400L) / 3600); 
    Serial.print(':');
    if ( ((epoch % 3600) / 60) < 10 ) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      Serial.print('0');
    }
    // print the minute (3600 equals secs per minute)
    Serial.print((epoch  % 3600) / 60); 
    Serial.print(':');
    if ( (epoch % 60) < 10 ) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.println(epoch % 60); // print the second
  }
  // wait ten seconds before asking for the time again
  delay(10000);
}
 
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress& address)
{
  Serial.print("sending NTP packet to: ");
  Serial.println( address );
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
 
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  udp.beginPacket(address, 123); //NTP requests are to port 123
  udp.write(packetBuffer, NTP_PACKET_SIZE);
  udp.endPacket();
}

ESP8266 Wifi Scanner

This sketch demonstrates how to scan WiFi networks. ESP8266 is programmed in access point mode. All found WiFi networks will be printed in TTY serial window.

#include "ESP8266WiFi.h"
 
void setup() {
  Serial.begin(115200);
 
  // Set WiFi to station mode and disconnect 
  // from an AP if it was previously connected
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);
 
  Serial.println("Setup done");
}
 
void loop() {
  Serial.println("scan start");
 
  // WiFi.scanNetworks will return the number of networks found
  int n = WiFi.scanNetworks();
  Serial.println("scan done");
  if (n == 0)
    Serial.println("no networks found");
  else
  {
    Serial.print(n);
    Serial.println(" networks found");
    for (int i = 0; i < n; ++i)
    {
      // Print SSID and RSSI for each network found
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print(" (");
      Serial.print(WiFi.RSSI(i));
      Serial.print(")");
      Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*");
      delay(10);
    }
  }
  Serial.println("");
 
  // Wait a bit before scanning again
  delay(5000);
}

ESP32 Wifi Scanner

There are many different development software and tools which can be used for ESP32 programming [163]:

  • Arduino COre ( C++)
  • ESP-IDF (Espressif IoT Development Framework)
  • Mongoose OS
  • MicroPython
  • Simba Embedded Programming Platform
  • Lua
  • JacvaSript
  • mruby
  • BASIC

Of course, for programming ESP32 We can use all the previously described Arduino examples for sensors and actuators. But in our example, we will focus on programming in ESP-IDF, as this is the native Development Platform for ESP32. A detailed description of the installation of the development environment can be found here.

This example shows how to use the All Channel Scan or Fast Scan to connect to a Wi-Fi network. In the Fast Scan mode, the scan will stop as soon as the first network matching the SSID is found. In this mode, an application can set the threshold for the authentication mode and the Signal strength. Networks that do not meet the threshold requirements will be ignored. In the All Channel Scan mode, the scan will end after all the channels are scanned, and the connection will start with the best network. The networks can be sorted based on Authentication Mode or Signal Strength. The priority for the Authentication mode is: WPA2 > WPA > WEP > Open.

#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_event_loop.h"
#include "nvs_flash.h"
 
/*Set the SSID and Password via "make menuconfig"*/
#define DEFAULT_SSID CONFIG_WIFI_SSID
#define DEFAULT_PWD CONFIG_WIFI_PASSWORD
 
#if CONFIG_WIFI_ALL_CHANNEL_SCAN
#define DEFAULT_SCAN_METHOD WIFI_ALL_CHANNEL_SCAN
#elif CONFIG_WIFI_FAST_SCAN
#define DEFAULT_SCAN_METHOD WIFI_FAST_SCAN
#else
#define DEFAULT_SCAN_METHOD WIFI_FAST_SCAN
#endif /*CONFIG_SCAN_METHOD*/
 
#if CONFIG_WIFI_CONNECT_AP_BY_SIGNAL
#define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL
#elif CONFIG_WIFI_CONNECT_AP_BY_SECURITY
#define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SECURITY
#else
#define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL
#endif /*CONFIG_SORT_METHOD*/
 
#if CONFIG_FAST_SCAN_THRESHOLD
#define DEFAULT_RSSI CONFIG_FAST_SCAN_MINIMUM_SIGNAL
#if CONFIG_EXAMPLE_OPEN
#define DEFAULT_AUTHMODE WIFI_AUTH_OPEN
#elif CONFIG_EXAMPLE_WEP
#define DEFAULT_AUTHMODE WIFI_AUTH_WEP
#elif CONFIG_EXAMPLE_WPA
#define DEFAULT_AUTHMODE WIFI_AUTH_WPA_PSK
#elif CONFIG_EXAMPLE_WPA2
#define DEFAULT_AUTHMODE WIFI_AUTH_WPA2_PSK
#else
#define DEFAULT_AUTHMODE WIFI_AUTH_OPEN
#endif
#else
#define DEFAULT_RSSI -127
#define DEFAULT_AUTHMODE WIFI_AUTH_OPEN
#endif /*CONFIG_FAST_SCAN_THRESHOLD*/
 
static const char *TAG = "scan";
 
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
    switch (event->event_id) {
        case SYSTEM_EVENT_STA_START:
            ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START");
            ESP_ERROR_CHECK(esp_wifi_connect());
            break;
        case SYSTEM_EVENT_STA_GOT_IP:
            ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP");
            ESP_LOGI(TAG, "Got IP: %s\n",
                     ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
            break;
        case SYSTEM_EVENT_STA_DISCONNECTED:
            ESP_LOGI(TAG, "SYSTEM_EVENT_STA_DISCONNECTED");
            ESP_ERROR_CHECK(esp_wifi_connect());
            break;
        default:
            break;
    }
    return ESP_OK;
}
 
/* Initialize Wi-Fi as sta and set scan method */
static void wifi_scan(void)
{
    tcpip_adapter_init();
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
 
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
	ESP_LOGI(TAG, DEFAULT_SSID);
	ESP_LOGI(TAG, DEFAULT_PWD);
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = DEFAULT_SSID,
            .password = DEFAULT_PWD,
            .scan_method = DEFAULT_SCAN_METHOD,
            .sort_method = DEFAULT_SORT_METHOD,
            .threshold.rssi = DEFAULT_RSSI,
            .threshold.authmode = DEFAULT_AUTHMODE,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
}
 
void app_main()
{
    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK( ret );
 
    wifi_scan();
}

To properly set up Station mode, it is necessary to enter SSID and password. To enter these values, before compiling the program, run the command:

make menuconfig

and then

make all

or

make flash

ESP Application Layer

ESP application layer may offer simplified a vast number of services as known from the PC world and the Internet yet. The limitation is the RAM size, storage, number of concurrent connections and limited CPU capabilities. Response routines should be kept simple as ESP8266 is single-threaded and uses timers and interrupt system to handle WiFi tasks in the background.

ESP8266 Samples

Below we present a number of samples, introducing programming of the various scenarios with ESP8266.

ESP8266 Web Server Sample

This example can be compiled in Arduino IDE. It allows through the website to change the output state of PIN 4 and PIN 5 [164]. We can connect LED to these pins and change its state remotely using a web browser. Before compiling this example it is necessary to change these two lines, to enable the module to connect to the WIFI network:

const char* ssid = ".. put here your own SSID name ...";
const char* password = ".. put here your SSID password.. ";

Now please check in the serial console the ESp8266 IP number and connect with any browser to address: http://esp8266_ipnumber

// Load Wi-Fi library
#include <ESP8266WiFi.h>
 
// Replace with your network credentials
const char* ssid = ".. put here your own SSID name ...";
const char* password = ".. put here your SSID password.. ";
 
// Set web server port number to 80
WiFiServer server(80);
 
// Variable to store the HTTP request
String header;
 
// Auxiliar variables to store the current output state
String gpio5State = "off";
String gpio4State = "off";
 
// Assign output variables to GPIO pins
const int gpiopin5 = 5;
const int gpiopin4 = 4;
 
void setup() {
  Serial.begin(115200);
  // Initialize the output variables as outputs
  pinMode(gpiopin5, OUTPUT);
  pinMode(gpiopin4, OUTPUT);
  // Set outputs to LOW
  digitalWrite(gpiopin5, LOW);
  digitalWrite(gpiopin4, LOW);
 
  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("ESP8266 IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}
 
void loop(){
  WiFiClient client = server.available();   // Listen for incoming clients
 
  if (client) {                    // If a new client connects,
    Serial.println("New Client."); // print a message out in the serial port
    String currentLine = "";       // make a String to hold incoming data 
    while (client.connected()) {   // loop while the client's connected
      if (client.available()) {    // if there's bytes to read from the client,
        char c = client.read();    // read a byte, then
        Serial.write(c);           // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code
            // (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, 
            // then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
 
            // turns the GPIOs on and off
            if (header.indexOf("GET /5/on") >= 0) {
              Serial.println("GPIO 5 on");
              gpio5State = "on";
              digitalWrite(gpiopin5, HIGH);
            } else if (header.indexOf("GET /5/off") >= 0) {
              Serial.println("GPIO 5 off");
              gpio5State = "off";
              digitalWrite(gpiopin5, LOW);
            } else if (header.indexOf("GET /4/on") >= 0) {
              Serial.println("GPIO 4 on");
              gpio4State = "on";
              digitalWrite(gpiopin4, HIGH);
            } else if (header.indexOf("GET /4/off") >= 0) {
              Serial.println("GPIO 4 off");
              gpio4State = "off";
              digitalWrite(gpiopin4, LOW);
            }
 
            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" 
                   content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons 
            // Feel free to change the background-color and 
            // font-size attributes to fit your preferences
            client.println("<style>html { font-family: Helvetica; display: 
                    inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #195B6A; 
                    border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; 
                    font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #77878A;}</style></head>");
 
            // Web Page Heading
            client.println("<body><h1>ESP8266 Web Server</h1>");
 
            // Display current state, and ON/OFF buttons for GPIO 5  
            client.println("<p>GPIO 5 - State " + gpio5State + "</p>");
            // If the output5State is off, it displays the ON button       
            if (gpio5State=="off") {
              client.println("<p><a href=\"/5/on\"><button 
                   class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/5/off\"><button 
                   class=\"button button2\">OFF</button></a></p>");
            } 
 
            // Display current state, and ON/OFF buttons for GPIO 4  
            client.println("<p>GPIO 4 - State " + gpio4State + "</p>");
            // If the output4State is off, it displays the ON button       
            if (gpio4State=="off") {
              client.println("<p><a href=\"/4/on\"><button 
                   class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/4/off\"><button 
                   class=\"button button2\">OFF</button></a></p>");
            }
            client.println("</body></html>");
 
            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything 
                                 // else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

After connecting with web browser to ESP8266 there will be such web page (figure 278), and we can change the input status of PIN 4 and 5 simply by pressing the appropriate button

Figure 278: ESP8266 web page
ESP32 Samples

ESP32 “Hello World”

This is simple program printing “Hello World” and it is written in Espressif IoT Development Framework

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
 
void app_main()
{
    printf("Hello world!\n");
 
    /* Print chip information */
    esp_chip_info_t chip_info;
    esp_chip_info(&chip_info);
    printf("This is ESP32 chip with %d CPU cores, WiFi%s%s, ",
            chip_info.cores,
            (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
            (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
 
    printf("silicon revision %d, ", chip_info.revision);
 
    printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
            (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? 
                  "embedded" : "external");
 
    for (int i = 10; i >= 0; i--) {
        printf("Restarting in %d seconds...\n", i);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    printf("Restarting now.\n");
    fflush(stdout);
    esp_restart();
}

ESP32 Web Server

This example of ESP32 programming in Arduino and shows how to implement simple www server.

First we do a little initialisation

//################# LIBRARIES ################
#include <WiFi.h>
#include <ESP32WebServer.h>
#include <WiFiClient.h>
//################ VARIABLES ################

String webpage = ""; // General purpose variable to hold HTML code
const char* ssid      = "ssdi";     // WiFi SSID
const char* password  = "password"; // WiFi Password

int status = WL_IDLE_STATUS;
int curr_index;
String SensorStatusBME;

// Site's Main Title
String siteheading    = "ESP32 Webserver";    
// Sub-heading for all pages
String subheading     = "Sensor Readings";    
// Appears on the tabe of a Web Browser
String sitetitle      = "ESP32 Webserver";    
// A foot note e.g. "My Web Site"
String yourfootnote   = "ESP32 Webserver Demonstration"; 
// Version of your Website
String siteversion    = "v1.0";  

Then we must implement the main www server activities. Mind, to access the server from outside of your network WiFi (LAN) e.g. on port 80 when in NAT mode, add a rule on your router that forwards a connection request to http://your_network_WAN_address:80 to http://your_network_LAN_address:80 and then you can access your ESP server from virtually anywhere on the Internet.

ESP32WebServer server(80); 
void setup()
{
  Serial.begin(115200); // initialize serial communications
  curr_index = 1;
 
  time_to_measure = millis();
 
  StartWiFi(ssid, password);
  StartTime();
  //----------------------------------------------------------------------
  Serial.println("To connect, uss: http://" + WiFi.localIP().toString() + "/"); 
  // If the user types at their browser 
  // http://192.168.0.100/ control is passed here and then 
  // to user_input, you get values for your program...
  server.on("/",          homepage);   
  // If the user types at their browser 
  // http://192.168.0.100/homepage or via menu control 
  // is passed here and then to the homepage, etc
  server.on("/homepage",  homepage);   
 
  // If the user types something that is not supported, say so
  server.onNotFound(handleNotFound);   
  // Start the webserver
  server.begin(); Serial.println(F("Webserver started...")); 
}
 
 
void handleNotFound() {
  String message = "The request entered could not be found, 
                    please try again with a different option\n";
  server.send(404, "text/plain", message);
}
 
void homepage() {
  append_HTML_header();
  webpage += "<P class='style2'>This is the server home page</p><br>";
  webpage += "<p class='style2'>";
  webpage += "This is sample webpage";
  webpage += "</p><br>";
  webpage += "<p>This page was displayed on : " + GetTime() + " Hr</p>";
  String Uptime = (String(millis() / 1000 / 60 / 60)) + ":";
  Uptime += (((millis() / 1000 / 60 % 60) < 10) ? "0" + 
               String(millis() / 1000 / 60 % 60) : 
               String(millis() / 1000 / 60 % 60)) + ":";
  Uptime += ((millis() / 1000 % 60) < 10) ? "0" + 
               String(millis() / 1000 % 60) : 
               String(millis() / 1000 % 60);
  webpage += "<p>Uptime: " + Uptime + "</p>";
  append_HTML_footer();
  server.send(200, "text/html", webpage);
}
 
void page1() {
 
  append_HTML_header();
  webpage += "<H3>This is the server Page-1</H3>";
  webpage += "<P class='style2'>This is the server home page</p>";
  webpage += "<p class='style2'>";
  webpage += "This is sample 1 page";
  webpage += "</p>";
  append_HTML_footer();
  server.send(200, "text/html", webpage);
}

next we must start Wifi :

void StartWiFi(const char* ssid, const char* password) {
  int connAttempts = 0;
  Serial.print(F("\r\nConnecting to: ")); Serial.println(String(ssid));
  WiFi.begin(ssid, password);

  status = WiFi.status();

  while (status != WL_CONNECTED ) {
    Serial.print(".");
    // wait 10 second for re-trying
    delay(10000);
    status = WiFi.status();
    Serial.println(status);
    if (connAttempts > 5) {
      Serial.println("Failed to connect to WiFi");
      //  printWiFiStatus();
    }
    connAttempts++;
  }
  Serial.print(F("WiFi connected at: "));
  Serial.println(WiFi.localIP());
}

and last step is to implement main loop function:

void loop() {
  
  delay( 2000 );  
  server.handleClient();

}

ESP32 Parallel Programming

As it is known, some of the microcontrollers, in order to increase performance provide more than one core. ESP32 is one of them providing two physical cores. In practice, it means that the program developed can run simultaneously on both cores. Thereby it is possible to optimize some of the tasks in a way that they are not waiting for each other but running in parallel instead. This is the main advantage of parallel programming comparing to a sequential one. However, it requires both dedicated program control structures and hardware support.

At the time while this chapter is being written, the simplest way of developing a parallel code on ESP32 is via using FreeRTOS™ [165], which is a widely used real-time library for different microcontrollers. The RTOS allows using most of the real-time and parallel programming features including semaphores, process assignments to cores and more. The following code chunks explain how to apply the most useful parallel programming features.

Let's start with an example of blinking LED and Text output (based on material found here [166]).

The first task is task1, that simply outputs a string “Hi there!” to default serial port with delay of 100 ms, i.e. 10 times per second:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/gpio.h"
 
#define BLINK_GPIO 13
 
void task1_SayHi(void * parameters)
{ 
   while(1)
   {
     printf("Hi there!\n");
     vTaskDelay(100 / portTICK_RATE_MS);
   }
}
  • #include “freertos/FreeRTOS.h” and #include “freertos/task.h” – adds needed libraries of FreeRTOS™.
  • #define BLINK_GPIO 13 – defines output pin that will be used to switch on or off the LED.
  • portTICK_RATE_MS refers to constant portTICK_PERIOD_MS that is used to calculate real-time from the tick rate – with the resolution of one tick period.

The second task is to bilk a LED with a period of 2 seconds (1 second on, 1 second off):

void task2_BlinkLED(void * parameters)
{
 
    gpio_pad_select_gpio(BLINK_GPIO);
    gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
    while(1) {
        /*Sets the LED low for one second*/
        gpio_set_level(BLINK_GPIO, 0);
        vTaskDelay(1000 / portTICK_RATE_MS);
 
        /*Sets the LED high for one second*/
        gpio_set_level(BLINK_GPIO, 1);
        vTaskDelay(1000 / portTICK_RATE_MS);
    }
}

Once both task functions are defined, they can be executed simultaneously:

void app_main()
{
    nvs_flash_init();
    xTaskCreate(&task1_SayHi, "task1_SayHi", 2000, NULL, 5, NULL);
    xTaskCreate(&task2_BlinkLED, "task2_BlinkLED", 2000,NULL,5,NULL );
}
  • nvs_flash_init() – initializes a non-volatile memory in flash memory, so it can be used by concurrent tasks
  • xTaskCreate – creates a task, without specifying a core, on which it is executed, with rather low priority (5). More on parameters can be found here [167].
In fact, the code does not run in parallel physically, it uses the full speed of the ESP32 that is far beyond human perception speed and shares the computation time between both tasks. Therefore for the human, it seems to be running in parallel. Each of the tasks uses Idle (defined by vTaskDelay()) time of the other task. Since both are simply the time slot is enough to complete.

To run the code physically in parallel it is necessary to assign task explicitly to the particular core, which requires a slight modification of the main() function:

void app_main()
{
    nvs_flash_init();
    xTaskCreatePinnedToCore(&task1_SayHi, "task1_SayHi", 2000, NULL, 5, NULL,0);
    xTaskCreatePinnedToCore(&task2_BlinkLED, "task2_BlinkLED", 2000,NULL,5,NULL,1);
}
  • xTaskCreatePinnedToCore – creates a task and assigns it to the particular core, on which it is executed. In this case, task1_SayHi() is assigned to core 0, while task2_BlinkLED() to core 1. For more information refer to [168].

While ESP32 provide two computing nodes, other devices like particular serial port or other peripherals are only single devices. In some cases, it might be needed to access those devices by multiple processes in a way that does not disturb the others. In a terminology of parallel programming, those “single” devices are called resources that need to be shared or simply shared resources. To share a resource it is necessary to have a signal that is available to all processes and that determines if the resource is available or not. Those signals are dedicated data structures and are called - semaphores. Depending on the particular platform they might represent a different data structure to address particular use case. RTOS support three main semaphore types – Binary (True/False), Counting (represents a queue) and Mutex (binary semaphore with priority). More details on each type and use examples might be found here [169]. To explain the concept of resource sharing here a simple binary-semaphore example is provided. Example uses two SayHi tasks to share the same output device:

Since we need to define a semaphore at the beginning a setup function is also needed:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/gpio.h"
 
SemaphoreHandle_t xSemaphore = NULL;
 
void setup()
{
    vSemaphoreCreateBinary( xSemaphore );
}

Now it is possible to define the task functions and modify them in a way they use the same resource

void task1_SayHi(void * parameters)
{ 
   while(1)
   {
       /*check and waits for semaphore to be released for 100 ticks. 
       If the semaphore is available it is taken / blocked */
       if( xSemaphoreTake( xSemaphore, ( TickType_t ) 100 ) == pdTRUE )
       {
          printf("TASK1: Hi there!\n");
          vTaskDelay(100 / portTICK_RATE_MS);
          xSemaphoreGive( xSemaphore );
       }
       else
       {
           //Does something else in case the semaphore is not available
       } 
   }
}
 
void task2_SayHi(void * parameters)
{ 
   while(1)
   {
       /*check and waits for semaphore to be released for 100 ticks. 
       If the semaphore is available it is taken / blocked */
       if( xSemaphoreTake( xSemaphore, ( TickType_t ) 100 ) == pdTRUE )
       {
          printf("TASK2: Hi there!\n");
          vTaskDelay(100 / portTICK_RATE_MS);
          xSemaphoreGive( xSemaphore );
       }
       else
       {
           //Does something else in case the semaphore is not available
       } 
   }
}

Now both of the tasks are ready to be executed on the same or different cores as explained previously.

Please note that semaphore mechanism is a powerful tool to synchronize tasks, prioritize tasks or simply make sure that a single resource is used properly in a multi-task application.

Raspberry Pi Overview

Raspberry Pi (referred as RPi or RPI) and its clones, i.e. Orange Pi, Banana Pi, Ordroid, Cubie, Olimex, are the class of devices located somewhere between low constraint IoT boards and regular PC/Mac machines.

While authors create this text, Raspberry Pi is a standard and reference solution for other manufacturers. However 3rd party manufacturers indeed offer more powerful solutions (regarding processor power, RAM size, connectivity capabilities, and integrated flash) usually by the cost of no support and not so well tailored operating system that lacks many features and present serious bugs.

Those devices technically are very close to smartphones and are far away from energy-efficient IoT solutions powered by a single battery that lasts for weeks or even years. They need DC, usually 5 or 12V and about 2-3W total, with external power adapter. It is still far less than even most efficient ultrabooks or PCs, requiring some 50-90W PSUs. They also use an operating system booted from storage like regular PCs - usually from flashed MicroSD card or embedded eMMC flash. The OS is mostly Linux based, but there do exist Microsoft Windows for certified Raspberry Pi devices. It is how this class of devices differ from, i.e. Arduino, where software is in the SoC model. The RPi and clones are holding a one-board solution that includes a processor, memory, storage slot, USB and networking. Many devices also offer hardware-based graphics acceleration, usually integrated with the processor core. Some devices like Orange Pi frequently provide an integrated flash for OS storage, so you do not necessarily need to boot and use an external flash like a USB dongle or TransFlash card. The most common processor in this class of the devices is an ARM architecture family, in case of the RPI it is Broadcom (i.e. BCM2936), other manufacturers use, i.e. Exynos, All-Winner and Samsung manufactured processors. What is pretty similar to the low-power, constrained IoT boards, RPi and clones offer GPIO, and you can connect various sensors and expansion boards (called here “hats”), and you have a wide choice of operating systems and modules. You can also extend the hardware by connecting hats that offer to sense and to actuate but sometimes advanced computing like dedicated coprocessors or FPGA-based AI. Interestingly, their GPIOs usually provide (among others) popular protocols like I2C, SPI, One-Wire, so you can directly connect with many sensors known as designed for Arduino-compatible development boards. This way, you can use those boards like conventional IoT devices with integrated networking capabilities, similar to, i.e. ESP chips.

What is much different from low constrained IoT devices is that they offer at least a command terminal you can connect to, and also most boards offer a capability to connect it to the external display via HDMI, analogue output or dedicated connector for LCD. They also provide the ability to interact with HID devices like regular keyboards, mouses, via USB but also wireless, i.e. using a Bluetooth connection. Of course, those features are dependent on operating systems. Manufacturers usually are trying to keep those development boards as small as possible, and it is a case that among high-end devices they also offer some constrained solution yet usually 50 % smaller in size and power consumption (i.e. RPi zero). Many boards also offer dedicated camera connector.

Being so far from the low-power, constrained IoT devices does not exclude them from IoT devices, however. They find their application everywhere, when there is a need for higher processing resources (i.e. voice recognition), high capacity and complex networking operations, i.e. gatewaying other devices to the Internet, convert networking protocols, implement software-based or hardware-assisted Artificial Intelligence, implementing rich user interface (GUI) where constrained devices are not powerful enough to fulfil the requirements yet there is still a limited power source, or there is not a need to set up a regular, PC-based solution, because of its cost. Most of the devices belonging to this class still can be switched to low power consumption modes, where low power means a dozen mA here.

On the other hand, most modern representatives of those devices are powered with multicore processors and large RAM and are powerful enough to replace the desktop computer in daily operations like web browsing, multimedia playback, software development and so on.

Raspberry Pi General Information

The Raspberry Pi is a series of small single-board computers developed in the UK by the Raspberry Pi Foundation to promote modern computer science in schools and developing electronic communities. Adding the 40-pin GPIO connector to the computer board allows developers not only improving their programming skills but also open them new horizons in controlling processes and devices not available for desktop computers. According to the Raspberry Pi Foundation, the entire boards' sales in July 2017 has reached nearly 15 million units. The first generation of this new board type was developed and then released in February 2012 – Raspberry Pi Model B. Each Raspberry Pi board contains hardware modules which together makes it fully usable PC like a computer which size fits the typical credit card (85/56 mm) size and small power consumption < 3.5 W. This makes this kind of single board computers one of the most popular in developers community. For today there exist thousands of hardware implementation projects available for users who want to learn the modern hardware and software controlling units within their projects. The general Raspberry Pi features are listed below.

Hardware

Hardware boards (depending on the manufactured model) contains interfaces: Ethernet, Bluetooth, WiFi, USB, AUDIO, HDMI and GPIO ports [170]. The Raspberry Pi boards have evolved through several versions varying in memory capacity, System on Chips (SoC) and processor units. First generation models of Raspberry Pi used the Broadcom BCM2835 (ARMv6 architecture) based on 700 MHz ARM11176JZF-S processor and VideoCore IV graphics processing Unit (GPU). Models Pi 1 and B+ developed later uses the five-point USB/Ethernet hub chip while the Pi 1 Model B only contains two. On the Pi Zero, the USB port is connected directly to the SoC and uses the (OTG) micro USB port.

Processor

The first Raspberry Pi 2 models use the 900 MHz Broadcom BCM2836 SoC 32-bit quad-core ARM Cortex-A7 processor, with shared 256 KB L2 cache. After this earlier models, the Raspberry Pi 2 V1.2 has been upgraded to a Broadcom BCM2837 SoC equipped with a 1.2 GHz 64-bit quad-core ARM Cortex-A53 processor. Latest Raspberry Pi 3 series uses the same SoC. They use the Broadcom BCM2837 SoC with a 1.2 GHz 64-bit quad-core ARM Cortex-A53 processor, equipped with 512 KB shared L2 cache. The Raspberry Pi 3B+ uses the same processor (BCM2837B0) but running at 1.4 GHz. Next Raspberry Pi generations are going to be more and more powerful, but their power consumption is still rising to force developers to use CPU and GPU heatsinks.

RAM

Older B board models were designed with 128 MB RAM which was by default allocated between the GPU and CPU. The Model B (including Model A) release the RAM was extended to 256 MB split to there regions. The default split was 192 MB (RAM for CPU), which is sufficient for standalone 1080p video decoding, or for 3D modelling. Models B with 512 MB RAM initially, memory was split to files released (arm256_start.elf, arm384_start.elf, arm496_start.elf) for 256 MB, 384 MB and 496 MB CPU RAM (and 256 MB, 128 MB and 16 MB video RAM). The Raspberry Pi 2 and 3 are shipped with 1 GB of RAM. The Raspberry Pi Zero and Zero W contains 512 MB of RAM.

Networking

The Model A, A+ and Pi Zero have no dedicated Ethernet interface and can be connected to a network using an external USB Ethernet or WiFi adapter. In Models B and B+, the Ethernet port is built-in to the USB Ethernet adapter using the SMSC LAN9514 chip. The Raspberry Pi 3 and Pi Zero W (wireless) models are equipped with 2.4 GHz WiFi 802.11n (150 Mbit/s) and Bluetooth 4.1 (24 Mbit/s) based on Broadcom BCM43438 FullMAC chip. The Raspberry Pi 3 also has a 10/100 Ethernet port.

Peripherals

The Raspberry Pi may be controlled with any generic USB computer keyboard and mouse. It can also use USB storage, USB to MIDI converters, and virtually any other device/component which is USB compatible. Other peripherals can be attached through the various pins and connectors on the surface of the Raspberry Pi.

Video

The video controller supports standard modern TV resolutions, such as HD and Full HD, and higher. It can emit 640 × 350 EGA; 640 × 480 VGA; 800 × 600 SVGA; 1024 × 768 XGA; 1280 × 720 720p HDTV; 1280 × 768 WXGA variant; 1280 × 800 WXGA variant; 1280 × 1024 SXGA; 1366 × 768 WXGA variant; 1400 × 1050 SXGA+; 1600 × 1200 UXGA; 1680 × 1050 WXGA+; 1920 × 1080 1080p HDTV; 1920 × 1200 WUXGA. Higher resolutions, such as, up to 2048 × 1152, may work or even 3840 × 2160 at 15 Hz. Although the Raspberry Pi 3 does not include H.265 hardware decoders, the CPU is more powerful than its predecessors, potentially fast enough for software decode H.265-encoded videos. The Raspberry Pi 3 GPU runs at a higher clock frequency – 300 MHz or 400 MHz, compared to 250 MHz previous versions. The Raspberry Pis is capable of generating 576i and 480i composite video signals, as used on old-style (CRT) TV screens and less-expensive monitors through standard connectors – either RCA or 3.5 mm phono connector depending on models. The television signal standards supported are PAL-BGHID, PAL-M, PAL-N, NTSC and NTSC-J.

Real-Time Clock

None of the current Raspberry Pi models is equipped with a built-in real-time clock. Developers which needs the real clock time in their project can retrieve the time from a network time server (NTP) or use the external RTC module connected to the board via SPI or I²C interface. To save the file system consistency of time, the Raspberry Pi automatically saves the time on shutdown, and reload it time at boot. One of the best RTC solutions for keeping the proper boards time is to use the I²C DS1307 chip containing hardware clock with battery power supply.

Specification

Table 21: Raspberry Pi Models A Comparative Table
Version Model A
RPi 1 Model A RPi 1 Model A+ RPi 3 Model A+
Release date 2/1/201311/1/201411/1/2018
Target price (USD) 252025
Instruction set ARMv6Z (32-bit)ARMv8 (64-bit)
SoC Broadcom BCM2835Broadcom BCM2837B0
FPU VFPv2; NEON not supportedVFPv4 + NEON
CPU 1× ARM1176JZF-S 700 MHz4× Cortex-A53 1.4 GHz
GPU Broadcom VideoCore IV @ 250 MHz (BCM2837: 3D part of GPU @ 300 MHz, video part of GPU @ 400 MHz)
OpenGL ES 2.0 (BCM2835, BCM2836: 24 GFLOPS / BCM2837: 28.8 GFLOPS)
MPEG-2 and VC-1 (with license), 1080p30 H.264/MPEG-4 AVC high-profile decoder and encoder (BCM2837: 1080p60)
Memory (SDRAM) 256 MB (shared with GPU) 512 MB (shared with GPU) as of 4 May 2016. Older boards had 256 MB (shared with GPU)
USB 2.0 ports 1 (direct from BCM2835 chip) 1 (direct from BCM2837B0 chip)
Video input 15-pin MIPI camera interface (CSI) connector, used with the Raspberry Pi camera or Raspberry Pi NoIR camera
Video outputs HDMI (rev 1.3) composite video (RCA jack), MIPI display interface (DSI) for raw LCD panelsHDMI (rev 1.3), composite video (3.5 mm TRRS jack), MIPI display interface (DSI) for raw LCD panels
Audio inputs As of revision 2 boards via I²S
Audio outputs Analog via 3.5 mm phone jack; digital via HDMI and, as of revision 2 boards, I²S
On-board storage SD, MMC, SDIO card slot (3.3 V with card power only)MicroSDHC slot
On-board network None2.4 GHz and 5 GHz IEE 802.11.b/g/n/ac wireless LAN, Bluetooth 4.2/BLE
Low-level peripherals 8× GPIO plus the following, which can also be used as GPIO: UART, I²C bus, SPI bus with two chip selects, I²S audio +3.3 V, +5 V, ground17× GPIO plus the same specific functions, and HAT ID bus
Power ratings 300 mA (1.5 W)200 mA (1 W)
Power source 5 V via MicroUSB or GPIO header
Size 85.60 mm × 56.5 mm (3.370 in × 2.224 in), excluding protruding connectors65 mm × 56.5 mm × 10 mm (2.56 in × 2.22 in × 0.39 in), same as HAT board65 mm x 56.5 mm
Weight 31 g (1.1 oz)23 g (0.81 oz)
Console Adding a USB network interface via tethering or a serial cable with optional GPIO power connector
Generation 11 +3+
Obsolescence n/an/ain production until at least January 2023
Statement
Type Model A
Table 22: Raspberry Pi Models B Comparative Table
Version Model B
RPi 1 Model B RPi 1 Model B+ RPi 2 Model B RPi 2 Model B v1.2 RPi 3 Model B RPi 3 Model B+
Release date April–June 20127/1/20142/1/201510/1/20162/1/20163/14/2018
Target price (USD) 352535
Instruction set ARMv6Z (32-bit)ARMv7-A (32-bit)ARMv8-A (64/32-bit)
SoC Broadcom BCM2835Broadcom BCM2836Broadcom BCM2837Broadcom BCM2837B0
FPU VFPv2; NEON not supportedVFPv3 + NEONVFPv4 + NEON
CPU 1× ARM1176JZF-S 700 MHz4× Cortex-A7 900 MHz4× Cortex-A53 900 MHz4× Cortex-A53 1.2 GHz4× Cortex-A53 1.4 GHz
GPU Broadcom VideoCore IV @ 250 MHz (BCM2837: 3D part of GPU @ 300 MHz, video part of GPU @ 400 MHz)
OpenGL ES 2.0 (BCM2835, BCM2836: 24 GFLOPS / BCM2837: 28.8 GFLOPS)
MPEG-2 and VC-1 (with license), 1080p30 H.264/MPEG-4 AVC high-profile decoder and encoder (BCM2837: 1080p60)
Memory (SDRAM) 512 MB (shared with GPU) as of 4 May 2016. Older boards had 256 MB (shared with GPU) 1 GB (shared with GPU)
USB 2.0 ports 2 (via on-board 3-port USB hub)4 (via on-board 5-port USB hub)
Video input 15-pin MIPI camera interface (CSI) connector, used with the Raspberry Pi camera or Raspberry Pi NoIR camera
Video outputs HDMI (rev 1.3), composite video (RCA jack), MIPI display interface (DSI) for raw LCD panelsHDMI (rev 1.3), composite video (3.5 mm TRRS jack), MIPI display interface (DSI) for raw LCD panels
Audio inputs As of revision 2 boards via I²S
Audio outputs Analog via 3.5 mm phone jack; digital via HDMI and, as of revision 2 boards, I²S
On-board storage SD, MMC, SDIO card slotMicroSDHC slotMicroSDHC slot, USB Boot Mode
On-board network 10/100 Mbit/s Ethernet (8P8C) USB adapter on the USB hub10/100 Mbit/s Ethernet,10/100/1000 Mbit/s Ethernet (real speed max 300 Mbit/s),
802.11b/g/n single band 2.4 GHz wireless,802.11b/g/n/ac dual band 2.4/5 GHz wireless,
Bluetooth 4.1 BLEBluetooth 4.2 LS BLE
Low-level peripherals 8× GPIO plus the following, which can also be used as GPIO: UART, I²C bus, SPI bus with two chip selects, I²S audio +3.3 V, +5 V, ground.17× GPIO plus the same specific functions, and HAT ID bus
An additional 4× GPIO are available on the P5 pad if the user is willing to make solder connections
Power ratings 700 mA (3.5 W)200 mA (1 W) average when idle, 350 mA (1.75 W) maximum under stress (monitor, keyboard and mouse connected)220 mA (1.1 W) average when idle, 820 mA (4.1 W) maximum under stress (monitor, keyboard and mouse connected)300 mA (1.5 W) average when idle, 1.34 A (6.7 W) maximum under stress (monitor, keyboard, mouse and WiFi connected)459 mA (2.295 W) average when idle, 1.13 A (5.661 W) maximum under stress (monitor, keyboard, mouse and WiFi connected)
Power source 5 V via MicroUSB or GPIO header
Size 85.60 mm × 56.5 mm (3.370 in × 2.224 in), excluding protruding connectors85.60 mm × 56.5 mm × 17 mm (3.370 in × 2.224 in × 0.669 in)
Weight 45 g (1.6 oz)
Console Adding a USB network interface via tethering or a serial cable with optional GPIO power connector
Generation 11 +22 ver 1.233+
Obsolescence n/an/an/an/an/ain production until at least January 2023
Statement
Type Model B
Table 23: Raspberry Pi Models Compute Module Comparative Table
Version Compute Module*
Compute Module 1 Compute Module 3 Compute Module 3 lite Compute Module 3+
Release date 4/1/20141/1/20171/1/2019
Target price (USD) $30 (in batches of 100)3025
Instruction set ARMv6Z (32-bit)ARMv8-A (64/32-bit)
SoC Broadcom BCM2835Broadcom BCM2837Broadcom BCM2837B0
FPU VFPv2; NEON not supportedVFPv4 + NEON
CPU 1× ARM1176JZF-S 700 MHz4× Cortex-A53 1.2 GHz
GPU Broadcom VideoCore IV @ 250 MHz (BCM2837: 3D part of GPU @ 300 MHz, video part of GPU @ 400 MHz)
OpenGL ES 2.0 (BCM2835, BCM2836: 24 GFLOPS / BCM2837: 28.8 GFLOPS)
MPEG-2 and VC-1 (with license), 1080p30 H.264/MPEG-4 AVC high-profile decoder and encoder (BCM2837: 1080p60)
Memory (SDRAM) 512 MB (shared with GPU)1 GB (shared with GPU)
USB 2.0 ports 1 (direct from BCM2835 chip)1 (direct from BCM2837 chip)
Video input 2× MIPI camera interface (CSI)
Video outputs HDMI, 2× MIPI display interface (DSI) for raw LCD panels, composite video
Audio inputs As of revision 2 boards via I²S
Audio outputs Analog, HDMI, I²S
On-board storage 4 GB eMMC flash memory chip MicroSDHC
On-board network None
Low-level peripherals 46× GPIO, some of which can be used for specific functions including I²C, SPI, UART, PCM, PWM
Power ratings 200 mA (1 W)700 mA (3.5 W)
Power source 5 V via MicroUSB or GPIO header
Size 67.6 mm × 30 mm (2.66 in × 1.18 in)67.6 mm × 31 mm (2.66 in × 1.22 in)
Weight 7 g (0.25 oz)
Console Adding a USB network interface via tethering or a serial cable with optional GPIO power connector
Generation 133 lite3+ lite
Obsolescence n/an/an/aCM3+ will remain in production until at least January 2026
Statement
Type Compute Module*
Table 24: Raspberry Pi Models Zero Comparative Table
Version Zero
RPi Zero PCB v1.2 RPi Zero PCB v1.3 RPi Zero W
Release date 11/1/20155/1/20162/28/2017
Target price (USD) 510
Instruction set ARMv6Z (32-bit)
SoC Broadcom BCM2835
FPU VFPv2; NEON not supported
CPU 1× ARM1176JZF-S 1 GHz
GPU Broadcom VideoCore IV @ 250 MHz (BCM2837: 3D part of GPU @ 300 MHz, video part of GPU @ 400 MHz)
OpenGL ES 2.0 (BCM2835, BCM2836: 24 GFLOPS / BCM2837: 28.8 GFLOPS)
MPEG-2 and VC-1 (with license), 1080p30 H.264/MPEG-4 AVC high-profile decoder and encoder (BCM2837: 1080p60)
Memory (SDRAM) 512 MB (shared with GPU)
USB 2.0 ports 1 Micro-USB (direct from BCM2835 chip)
Video input NoneMIPI camera interface (CSI)
Video outputs Mini-HDMI, 1080p60, composite video via marked points on PCB for optional header pins
Audio inputs As of revision 2 boards via I²S
Audio outputs Mini-HDMI, stereo audio through PWM on GPIO
On-board storage MicroSDHC
On-board network None 802.11b/g/n single band 2.4 GHz wireless,
Bluetooth 4.1 BLE
Low-level peripherals 17× GPIO plus the same specific functions, and HAT ID bus
Power ratings 100 mA (0.5 W) average when idle, 350 mA (1.75 W) maximum under stress (monitor, keyboard and mouse connected)
Power source 5 V via MicroUSB or GPIO header
Size 65 mm × 30 mm × 5 mm (2.56 in × 1.18 in × 0.20 in)
Weight 9 g (0.32 oz)
Console Adding a USB network interface via tethering or a serial cable with optional GPIO power connector
Generation PCB ver 1.2PCB ver 1.3W (wireless)
Obsolescence n/a, or see PCB ver 1.3Zero is currently stated as being not before January 2022n/a
Statement
Type Zero

Raspberry Pi Boards

As for today, on the market there are available few models of Raspberry Pi boards, from tiny ones to more powerful. User can choose the right board to fit the price and functionality to his project development needs. Below figures are listed form the tiny/cheap to most sophisticated Raspberry Pi models.

Figure 279: Raspberry Pi Zero [171].
Figure 280: Raspberry Pi 1 Model A.
Figure 281: Raspberry Pi 1 Model A+ revision 1.1 [172].
Figure 282: Raspberry Pi 1 Model B revision 1.2 [173].
Figure 283: Raspberry Pi 2 [174].
Figure 284: Raspberry Pi 3 [175].

General-Purpose Input-Output (GPIO) Connector

Each Raspberry Pi model is equipped with standard 34/40-pis male connector containing universal GPIO ports, VCC 3.3/5V, GND, CLK, I2C/SPI buses pins which developers can use to connect their external sensors, switches and other controlled devices to the Raspberry Pi board and then program their behaviour within the code loaded to the board.

  • Raspberry Pi 1 Models A+ and B+, Pi 2 Model B, Pi 3 Model B and Pi Zero (and Zero W) GPIO J8 have a 40-pin pinout. Raspberry Pi 1 Models A and B have only the first 26 pins.
Figure 285: Raspberry Pi 1 pins.
  • Model B rev. 2 also has a pad (called P5 on the board and P6 on the schematics) of 8 pins offering access to an additional 4 GPIO connections.
Figure 286: Raspberry Pi 2 & 3 pins.
HDMI Port

Each Raspberry Pi model is equipped with the standard mini HDMI port allows user connect the monitor or TV set with the board.The electronic schematic is shown on the picture.

Figure 287: Raspberry HDMI port connection schematic.

Camera Port CSI

Raspberry Pi boars Zero, 1, A+, 2, 3 are equipped with Camera interface (CSI) port allowing user connect the CCD camera following the MIPI standard.

Figure 288: Raspberry CSI camera schematic [176].
Figure 289: Raspberry CSI camera view [177].

Display Port (DSI)

Raspberry Pi boars 2, 3 are equipped with LCD Display interface(DSI) port allowing the user to connect the LCD touch display to the board. The official Raspberry Pi LCD touch display shown in the figure below is 800 x 480 dpi 7“ size can be connected to the Raspberry board using the DSI interface. Such an assembly can be used in the projects to display controlling application view and with the ability to handle fingers touchscreen controls the project behaviour. The LCD can be mounted in portrait/landscape orientation fitting the best user needs.

Figure 290: Raspberry DSI display port schematic [178].
Figure 291: Raspberry DSI LCD display kit [179].

USB and LAN Ports

Raspberry PI models boars Zero, 1, A+, 2, 3 contains USB ports (from 1 up to 4) and models boars 1, A+, 2, 3 the LAN port for TCP/IP network connections. This ports can be used for mouse/keyboard connection or if the software has appropriate driver installed to handle other USB devices.

Figure 292: Raspberry LAN/USB ports view [180].

Raspberry Pi Sensors

Raspberry Pi boards offer an easy way to connect different sensors and control devices. With specially designed I/O pins available to program them by developers the amount of possible implementations growth year by year. Any I/O General Purpose Input-Output Ports (GPIO) can be set as Digital Input or Output. The board contains two PWM pins which can be used as output analogue signals. Some of the interface libraries, such as pigpio or wiringPi, support this feature. It is also the way the Raspberry Pi outputs analogue audio.

Touch Sensors

Button

A pushbutton is an electromechanical sensor that connects or disconnects two points in a circuit when the force is applied. Button output discrete value is either HIGH or LOW.

A microswitch, also called a miniature snap-action switch, is an electromechanical sensor that requires a very little physical force and uses tipping-point mechanism. Microswitch has three pins, two of which are connected by default. When the force is applied, the first connection breaks and one of the pins is connected to the third pin.

The most common use of a push button is as an input device. Both force solutions can be used as simple object detectors, or as end switches in the industrial devices.

 title
Figure 293: A push button[181] and a microswitch [182].
 title
Figure 294: Schematics of Raspberry Pi and a push button.

To proper work with the button, the GPIO4 must be configured as an digital input. Pressing the push button connects the GPIO4 pin to the boards GND. On Raspberry Pi GPIO input pins are normally pulled up to 3.3 V. When the button is pressed, and GPIO4 is read using GPIO.input, it will return the FALSE result. Each GPIO pin can be configured to use internal pull-up or pull-down resistors. Using a GPIO pin as an input, these resistors can be configured using the optional pull_up_down parameter in the GPIO.setup. If this parameter is omitted, resistors will not be activated. In this case, the input may floating giving unpredicted results during reading it. If the GPIO pin is set to GPIO.UD_UP, the pull-up resistor is enabled; if it is set to GPIO.PUD_DOWN, the pull-down resistor is enabled.

An example code:

#Python code for Raspberry Pi
 
import RPi.GPIO as GPIO
import time
 
GPIO.setmode(GPIO.BCM)
s_pin = 7 #Select the GPIO4 pin
 
#Set the GPIO4 port to input mode
GPIO.setup(s_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) 
 
while True:
    input_state = GPIO.input(s_pin)
    if input_state == False:
        print('Button Pressed')
        time.sleep(0.2)

Running the code as superuser shows:

pi@raspberrypi ~ $ sudo python switch.py
Button Pressed
Button Pressed
Button Pressed
Button Pressed
Force Sensor

A force sensor predictably changes resistance, depending on the applied force to its surface. Force-sensing resistors are manufactured in different shapes and sizes, and they can measure not only direct force but also the tension, compression, torsion and other types of mechanical forces. The voltage is measured by applying and measuring constant voltage to the sensor.

Force sensors are used as control buttons or to determine weight.

 title
Figure 295: 0.5 inch force sensing resistor (FSR) [183].
 title
Figure 296: Raspberry Pi and Force Sensitive Resistor circuit schematics.

An example code:

#Python code for Raspberry Pi
 
import RPi.GPIO as GPIO
import time
 
GPIO.setmode(GPIO.BCM)
 
a_pin = 7   #Select the GPIO4 pin
b_pin = 29  #Select the GPIO5 pin
 
def discharge():
    GPIO.setup(a_pin, GPIO.IN)
    GPIO.setup(b_pin, GPIO.OUT)
    GPIO.output(b_pin, False)
    time.sleep(0.005)
 
def charge_time():
    GPIO.setup(b_pin, GPIO.IN)
    GPIO.setup(a_pin, GPIO.OUT)
    count = 0
    GPIO.output(a_pin, True)
    while not GPIO.input(b_pin):
        count = count + 1
    return count
 
def analog_read():
    discharge()
    return charge_time()
 
while True:
    print(analog_read())
    time.sleep(1)

Running the code as superuser shows:

$ sudo python pot_step.py
10
12
10
10
16
23
43
53
67
72
86
105
123
143
170

The idea of how to read the force sensor changing value is called step response. It works by checking how the circuit responds to the step change when an output is switched from low to high. Raspberry Pi isn't equipped with an ADC converter. So it is impossible to read voltage directly. However, it can be measured how long the capacitor will fill with the charge to the extent that it gets voltage above 1.65 V or so that constitutes a high digital input. The speed at which the capacitor fills with charge depends on the value of the variable resistor (Rt). The lower the resistance, the faster the capacitor fills with charge, and the voltage rises. To get the proper value, the circuit must empty the capacitor each time before the reading starts. In the schematic the GPIO4 is used to charge the capacitor and GPIO5 is used to discharge the capacitor through the 10 kΩ resistor. Both resistors are used to make sure that there is no way too much current can flow as the capacitor is charged and discharged. To discharge it, connection GPIO4 is set to be an input, effectively disconnecting Rc and Rt from the circuit. Connection GPIO5 is then set to be an output and low. It is held there for 5 milliseconds, to empty the capacitor.

Capacitive Sensor

Capacitive sensors are a range of sensors that use capacitance to measure changes in the surrounding environment. A capacitive sensor consists of a capacitor that is charged with a certain amount of current until the threshold voltage. A human finger, liquids or other conductive or dielectric materials that touch the sensor, can influence a charge time and a voltage level in the sensor. Measuring charge time and a voltage level gives information about changes in the environment.

Capacitive sensors are used as input devices and can measure proximity, humidity, fluid level and other physical parameters or serve as an input for electronic device control.

 title
Figure 297: Digital capacitive touch sensor v2.0 switch module [184].
 title
Figure 298: Raspberry Pi and capacitive sensor schematics.
#Python code for Raspberry Pi
 
import time
import pigpio #http://abyz.co.uk/rpi/pigpio/python.html
RXD=15        #Define the RxD serial input port
 
pi = pigpio.pi()
if not pi.connected:
   exit(0)
 
pigpio.exceptions = False #Ignore error if already set as bit bang read.
pi.bb_serial_read_open(RXD, 9600) #Set baud rate here.
pigpio.exceptions = True
pi.bb_serial_invert(RXD, 1) #Invert line logic.
stop = time.time() + 60.0
while time.time() < stop:
  (count, data) = pi.bb_serial_read(RXD)
  if count:
    print(data)
  time.sleep(0.2)
 
pi.bb_serial_read_close(RXD)
 
pi.stop()

Proximity and Distance Sensors

Ultrasound Sensor

Ultrasound (ultrasonic) sensor measures the distance to objects by emitting ultrasound and measuring its returning time. The sensor consists of an ultrasonic emitter and receiver; sometimes, they are combined in a single device for emitting and receiving. Ultrasonic sensors can measure greater distances and cost less than infrared sensors, but are more imprecise and interfere which each other measurement if more than one is used. Simple sensors have trigger pin and echo pin, when the trigger pin is set high for the small amount of time ultrasound is emitted and on echo pin, response time is measured. Ultrasonic sensors are used in car parking sensors and robots for proximity detection.

 title
Figure 299: Ultrasonic proximity sensor HC-SR04 [185].

Examples of IoT applications are robotic obstacle detection and room layout scanning.

 title
Figure 300: Raspberry Pi and ultrasound proximity sensor circuit.

An example code:

#Python code for Raspberry Pi
 
import RPi.GPIO as GPIO
import time
 
TRIG =  7  #Define a trigger pin GPIO4
ECHO = 29  #Define an echo pin GPIO5
 
print ("Distance Measurement In Progress")
 
GPIO.setup(TRIG, GPIO.OUT)     #Set the GPIO4 as trigger output port
GPIO.setup(ECHO,GPIO.IN)       #Set the GPIO5 pin as echo input
 
GPIO.output (TRIG,False)
 
print ("Waiting for Sensor to Settle")
time.sleep(2)
 
GPIO.output (TRIG, True)
time.sleep (0.00001)
GPIO.output (TRIG, False)
 
while GPIO.input(ECHO) == 0:
  pulse_start = time.time()
 
while GPIO.input(ECHO) == 1:
  pulse_end = time.time()
 
pulse_duration = pulse_end - pulse_start
distance = pulse_duration*17150
distance = round(distance,2)         #Calculating the distance
print ("Distance:", distance, "cm")

Running the code as superuser shows:

pi@raspberrypi > $ sudo python range_sensor.py
Distance Measurement To Settle
Distance: 23.54 cm
pi@raspberrypi > $
Motion Detector

The motion detector is a sensor that detects moving objects, most people. Motion detectors use different technologies, like passive infrared sensors, microwaves and Doppler effect, video cameras and previously mentioned ultrasonic and IR sensors. Passive IR sensors are the simplest motion detectors that sense people trough detecting IR radiation that is emitted through the skin. When the motion is detected, the output of a motion sensor is a digital HIGH/LOW signal.

Motion sensors are used for security purposes, automated light and door systems. As an example in IoT, the PIR motion sensor can be used to detect motion in security systems a house or any building.

 title
Figure 301: PIR motion sensor HC-SR501 [186].
 title
Figure 302: Raspberry Pi and PIR motion sensor circuit.

An example code:

#Python code for Raspberry Pi
 
pirPin = 7; //Passive Infrared (PIR) sensor output is connected to the GPIO4 pin
 
GPIO.setup(pirPin ,GPIO.IN)       #Set the GPIO5 pin as echo input
 
while 1:
  #Read the digital value of the PIR motion sensor GPIO4
  pirReading = GPIO.input(pirPin) 
  print (piReading)               #Print out
 
  if pirReading == True:          #Motion was detected
    print ('Motion Detected')
  time.sleep(10)
Gyroscope

A gyroscope is a sensor that measures the angular velocity. The sensor is made of the microelectromechanical system (MEMS) technology and is integrated into the chip. The output of the sensor can be either analogue or digital value of information, using I2C or SPI interface. Gyroscope microchips can vary in the number of axes they can measure. The available number of the axis is 1, 2 or 3 axes in the gyroscope. For gyroscopes with 1 or 2 axes, it is essential to determine which axis the gyroscope measures and to choose a device according to the project needs. A gyroscope is commonly used together with an accelerometer, to determine the orientation, position and velocity of the device precisely. Gyroscope sensors are used in aviation, navigation and motion control.

Gyroscope sensors are used in aviation, navigation and motion control.

 title
Figure 303: MPU 6050 GY-521 breakout board [187].
 title
Figure 304: Raspberry Pi and MPU 6050 GY-521 gyro breakout schematics.

The example code for the FXAS21002C sensor used in the breakout board:

#Python code for Raspberry Pi
#!/usr/bin/env python
 
from __future__ import division, print_function
from nxp_imu import IMU
import time
 
imu = IMU(gs=4, dps=2000, verbose=True)
header = 67
print('-'*header)
print("| {:17} | {:20} | {:20} |".format("Accels [g's]", " Magnet [uT]", "Gyros [dps]"))
print('-'*header)
for _ in range(10):
  a, m, g = imu.get()
  print('| {:>5.2f} {:>5.2f} {:>5.2f} | {:>6.1f} {:>6.1f} 
           {:>6.1f} | {:>6.1f} {:>6.1f} {:>6.1f} |'.format(
         a[0], a[1], a[2],
         m[0], m[1], m[2],
         g[0], g[1], g[2])
       )
  time.sleep(0.50)
print('-'*header)
print(' uT: micro Tesla')
print('  g: gravity')
print('dps: degrees per second')
print('')
Compass

A compass is a sensor, that can measure the orientation of the device to the magnetic field of the Earth. Solid state compass consists of the magnetometer and accelerometers in a single chip to precisely calculate the position of the device. Devices communicate through I2C or SPI interfaces and can return calculated heading, pitch and roll and raw accelerometer and magnetometer values. Compass is used in outdoor navigation for mobile devices, robots, quadcopters.

 title
Figure 305: Compass module HMC5883L [188].
 title
Figure 306: Raspberry Pi and Compass module HMC5883L schematics.

The example code:

1. Install i2c:
   sudo apt-get install i2c-tools
2. edit file /etc/modprobe.d/raspi-blacklist.conf 
       and comment out the line blacklist i2c-bcm2708
3. edit /etc/modules, and add the lines:
   i2c-bcm2708
   i2c-dev
4. Allow i2c access from users other than root, 
       by creating the file /etc/udev/rules.d/99-i2c.rules with this line:
   SUBSYSTEM=="i2c-dev", MODE="0666"
5. Reboot the Pi. When it goes up again, type:
   ls /dev/i2c*
On Pi (Model B, Revision 2 version, early 2013) it generates:
/dev/i2c-0 /dev/i2c-1
Optional: For python, install the smbus python library with:
1. apt-get install python-smbus
2. Install Python 3, can’t hurt, and i2clibraries needs it. 
        Just type sudo apt-get install python3
3. Test if the compass is detected, by typing:
   i2cdetect -y 1 (for Revision 1 Pis, replace 1 with 0).
4. Replace with 0 for Revision 1 Raspberry Pis and with 
        1 for Revision 2 boards. This is the output:
   0 1 2 3 4 5 6 7 8 9 a b c d e f
   00: -- -- -- -- -- -- -- -- -- -- -- -- --
   10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1e --
   20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
   30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
   40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
   50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
   60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
   70: -- -- -- -- -- -- -- --

Tip: if you don’t see it, it’s because you haven’t welded the pins to the sensor. Just press with your finger. Or weld it.
1. Add the quick2wire code. Pull from git:
   git clone https://github.com/quick2wire/quick2wire-python-api.git.
   On /etc/profile, add: export QUICK2WIRE_API_HOME=/home/pi/quick2wire-python-api
2. export PYTHONPATH=$PYTHONPATH:$QUICK2WIRE_API_HOME
   Add i2clibraries. Pull from git:git 
   clone https://bitbucket.org/thinkbowl/i2clibraries.git

Environment Sensors

Temperature Sensor

A temperature sensor is a device that is used to determine the temperature of the surrounding environment. Most temperature sensors work on the principle that the resistance of the material is changed depending on its temperature. The most common temperature sensors are:

  • thermocouple – consists of two junctions of dissimilar metals,
  • thermistor – includes the temperature-dependent ceramic resistor,
  • resistive temperature detector – is made of a pure metal coil.

The main difference between sensors is the measured temperature range, precision and response time. Temperature sensor usually outputs the analogue value, but some existing sensors have a digital interface [189].

The temperature sensors most commonly are used in environmental monitoring devices and thermoelectric switches. In IoT applications, the sensor can be used for greenhouse temperature monitoring, warehouse temperature monitoring to avoid frozen fire suppression systems and tracking temperature of the soil, water and plants.

 title
Figure 307: Thermistor sensor [190].
 title
Figure 308: Raspberry Pi and thermistor circuit.

An example code is similar to the Raspberry Pi force sensor sample. The thermistor changes its resistance depends on the environment temperature, and it can be read using similar code:

#Python code for Raspberry Pi
 
import RPi.GPIO as GPIO
import time
 
GPIO.setmode(GPIO.BCM)
 
a_pin = 7   #Select the GPIO4 pin
b_pin = 29  #Select the GPIO5 pin
 
def discharge():
    GPIO.setup(a_pin, GPIO.IN)
    GPIO.setup(b_pin, GPIO.OUT)
    GPIO.output(b_pin, False)
    time.sleep(0.005)
 
def charge_time():
    GPIO.setup(b_pin, GPIO.IN)
    GPIO.setup(a_pin, GPIO.OUT)
    count = 0
    GPIO.output(a_pin, True)
    while not GPIO.input(b_pin):
        count = count + 1
    return count
 
def analog_read():
    discharge()
    return charge_time()
 
while True:
    print(analog_read())
    time.sleep(1)
Humidity Sensor

A humidity sensor (hygrometer) is a sensor that detects the amount of water or water vapour in the environment. The most common principle of the air humidity sensors is the change of capacitance or resistance of materials that absorb the moisture from the environment. Soil humidity sensors measure the resistance between the two electrodes. The resistance between electrodes is influenced by soluble salts and water amount in the soil. The output of a humidity sensor is usually an analogue signal value [191].

Example IoT applications are monitoring of humidor, greenhouse temperature and humidity, agricultural environment and art gallery and museum environment.

 title
Figure 309: DHT11 temperature and humidity sensor breakout [192].
 title
Figure 310: Raspberry Pi and humidity sensor schematics.

An example code [193]:

1. Enter this at the command prompt to download the library:
   git clone https://github.com/adafruit/Adafruit_Python_DHT.git

2. Change directories with: 
   cd Adafruit_Python_DHT

3. Now enter this: 
   sudo apt-get install build-essential python-dev

4. Then install the library with: 
   sudo python setup.py install
#Python code for Raspberry Pi
#!/usr/bin/python
 
import sys
import Adafruit_DHT
 
while True:
    humidity, temperature = Adafruit_DHT.read_retry(11, 7) #Read GPIO4 Pin 7
    print ('Temp: {0:0.1f} C  Humidity: {1:0.1f} %'.format(temperature, humidity))
Sound Sensor

A sound sensor is a sensor that detects vibrations in a gas, liquid or solid environments. At first, the sound wave pressure makes mechanical vibrations, who transfers to changes in capacitance, electromagnetic induction, light modulation or piezoelectric generation to create an electric signal. The electrical signal is then amplified to the required output levels. Sound sensors, can be used to record sound, detect noise and its level.

Sound sensors are used in drone detection, gunshot alert, seismic detection and vault safety alarm.

 title
Figure 311: Digital sound detector sensor module [194].
 title
Figure 312: Raspberry Pi and sound sensor schematics.

An example code:

#Python code for Raspberry Pi
 
import time
import RPi.GPIO as GPIO
from qhue import Bridge
 
GPIO.setmode(GPIO.BCM) #Use board pin numbers
pin = 7                #Define GPIO4 as Input
GPIO.setup(pin, GPIO.IN) 
 
def callback (pin)
  if GPIO.input (pin)
    print ("Sound detected!")
  else:
    print ("Sound detected!")
  #Activate when pin changed its state
  GPIO.add_event_detect(pin,GPIO_BOTH, bouncetime=300) 
  #Assign function to GPIO PIN run it on changes
  GPIO.add_event_callback(pin,callback)                
 
#Infinite loop
while True:
 time.sleep(1)  
Chemical/Smoke and Gas Sensor

Gas sensors are a sensor group, that can detect and measure a concentration of certain gasses in the air. The working principle of electrochemical sensors is to absorb the gas and to create current from an electrochemical reaction. For process acceleration, a heating element can be used. For each type of gas, different kind of sensor needs to be used. Multiple different types of gas sensors can be combined in a single device as well. The single gas sensor output is an analogue signal, but devices with multiple sensors used to have a digital interface.

Gas sensors are used for safety devices, to control air quality and for manufacturing equipment. Examples of IoT applications are air quality control management in smart buildings and smart cities or toxic gas detection in sewers and underground mines.

 title
Figure 313: MQ2 gas sensor [195].
 title
Figure 314: Raspberry Pi and MQ2 gas sensor schematics.

An example code:

#Python code for Raspberry Pi

1. git clone https://github.com/tutRPi/Raspberry-Pi-Gas-Sensor-MQ
2. cd Raspberry-Pi-Gas-Sensor-MQ
3. sudo python example.py
#Python code for Raspberry Pi
#!/usr/bin/env python
 
import PCF8591 as ADC
import RPi.GPIO as GPIO
import time
import math
 
DO   = 17
Buzz = 18
GPIO.setmode(GPIO.BCM)
 
def setup():
  ADC.setup(0x48)
  GPIO.setup  (DO, GPIO.IN)
  GPIO.setup  (Buzz, GPIO.OUT)
  GPIO.output (Buzz, 1)
 
def Print(x):
  if x == 1:
    print ('')
    print ('   *********')
    print ('   * Safe~ *')
    print ('   *********')
    print ('')
    if x == 0:
      print ('')
      print ('   ***************')
      print ('   * Danger Gas! *')
      print ('   ***************')
      print ('')
 
def loop():
  status = 1
  count = 0
  while True:
    print (ADC.read(0))
 
    tmp = GPIO.input(DO);
    if tmp != status:
      print(tmp)
      status = tmp
      if status == 0:
        count += 1
        if count % 2 == 0:
          GPIO.output(Buzz, 1)
        else:
          GPIO.output(Buzz, 0)
      else:
	GPIO.output(Buzz, 1)
	count = 0
 
time.sleep(0.2)
 
def destroy():
  GPIO.output(Buzz, 1)
  GPIO.cleanup()
 
if __name__ == '__main__':
  try:
    setup()
    loop()
    except KeyboardInterrupt: 
    destroy()

Other Sensors

Global Positioning System

A GPS receiver is a device, that can receive information from a global navigation satellite system and calculate its position on the Earth. GPS receiver uses a constellation of satellites and ground stations to compute position and time almost anywhere on the Earth. GPS receivers are used for navigation only in the outdoor area because it needs to receive signals from the satellites. The precision of the GPS location can vary.

A GPS receiver is used for device location tracking. Real world applications might be pet, kid or personal belonging location tracking.

 title
Figure 315: LS20031 GPS receiver [196].
 title
Figure 316: Raspberry Pi and LS20031 GPS receiver schematics.

The example code:

#Python code for Raspberry Pi
#!/usr/bin/python
 
import os
import pygame, sys
from pygame.locals import *
import serial
 
#Initialise serial port on /ttyUSB0
ser = serial.Serial('/dev/ttyUSB0',4800,timeout = None)
#Set font size MAX 100
fontsize = 50
 
#Calculate window size
width = fontsize * 17
height = fontsize + 10
 
#Initilaise pygame
pygame.init()
windowSurfaceObj = pygame.display.set_mode((width,height),1,16)
fontObj = pygame.font.Font('freesansbold.ttf',fontsize)
pygame.display.set_caption('GPS Location')
redColor = pygame.Color(255,0,0)
greenColor = pygame.Color(0,255,0)
yellowColor = pygame.Color(255,255,0)
blackColor = pygame.Color(0,0,0)
 
fix = 1
color = redColor
x = 0
while x == 0:
  gps = ser.readline()
  #Print (all NMEA strings)
  print (gps)
  #Check gps fix status
  if gps[1:6] == "GPGSA":
    fix = int(gps[9:10])
    if fix == 2:
      color = yellowColor
    if fix == 3:
      color = greenColor
   #Print (time, lat and long from #GPGGA string)
    if gps[1 : 6] == "GPGGA":
      #Clear window
      pygame.draw.rect(windowSurfaceObj,blackColor,Rect(0,0,width,height))
      pygame.display.update(pygame.Rect(0,0,width,height))
      #Get time
      time = gps[7:9] + ":" + gps[9:11] + ":" + gps[11:13]
      #If 2 or 3D fix get lat and long
      if fix > 1:
        lat = " " + gps[18:20] + "." + gps[20:22] + "." + gps[23:27] + gps[28:29]
        lon = " " + gps[30:33] + "." + gps[33:35] + "." + gps[36:40] + gps[41:42]
      #If no fix
      else:
        lat = " No Valid Data "
        lon = " "
      #Print new values   
      msgSurfaceObj = fontObj.render(str(time), False,color)   
      msgRectobj = msgSurfaceObj.get_rect()
      msgRectobj.topleft =(2,0)
      windowSurfaceObj.blit(msgSurfaceObj, msgRectobj)
 
      msgSurfaceObj = fontObj.render(str(lat), False,color)   
      msgRectobj = msgSurfaceObj.get_rect()
      msgRectobj.topleft =(210,0)
      windowSurfaceObj.blit(msgSurfaceObj, msgRectobj)
 
      msgSurfaceObj = fontObj.render(str(lon), False,color)   
      msgRectobj = msgSurfaceObj.get_rect()
      msgRectobj.topleft =(495,0)
      windowSurfaceObj.blit(msgSurfaceObj, msgRectobj)
      pygame.display.update(pygame.Rect(0,0,width,height))
      fix = 1
      color = redColor
 
   #Check for ESC key pressed, or GPS Location window closed, to quit
   for event in pygame.event.get():
     if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
       pygame.quit()
       sys.exit()
}

Raspberry Pi Drivers and Driving

Optical Device Drivers and Their Devices

Light-Emitting Diode

The light-emitting diode also called LED is a special type of diodes which emits light, unlike the other diodes. LED has a completely different body which is made of transparent plastic that protects the diode and lets it emit light. Like the other diodes LED conducts the current in only one way, so it is essential to connect it to the scheme correctly. There are two safe ways how to determine the direction of the diode:

  • in the cathodes side of the diode its side is chipped,
  • anodes leg usually is longer than the cathodes leg.
 title
Figure 317: White LED [197].

The LED is one of the best light sources. Unlike incandescent light bulb LED transforms most of the power into light, not warmth; it is more durable, works for a more extended period and can be manufactured in a smaller size.

The LED colour is determined by the semiconductors material. Diodes are usually made from silicon then LEDs are made from elements like gallium phosphate, silicon carbide and others. Because the semiconductors used are different, the voltage needed for the LED to shine is also different. In the table, you can see with which semiconductor you can get a specific colour and the voltage required to turn on the LED.

When LED is connected to the voltage and turned on a huge current starts to flow through it, and it can damage the diode. That is why all LEDs have to be connected to current limiting resistor.

Current limiting resistors resistance is determined by three parameters:

  • I_D – current that can flow through the LED,
  • U_D – Voltage that is needed to turn on the LED,
  • U – combined voltage for LED and resistor.

To calculate the resistance needed for a diode, this is what you have to do.

  1. Find out the voltage needed for the diode to work UD; you can find it in the diodes parameters table.
  2. Find out the amperage needed for the LED to shine ID; it can be found in the LEDs datasheet, but if you can’t find it then 20 mA current is usually a correct and safe choice.
  3. Find out the combined voltage for the LED and resistor; usually, it is the feeding voltage for the scheme.
  4. Insert all the values into this equation: R = (U – U_D) / I_D.
  5. You get the resistance for the resistor for the safe use of the LED.
  6. Find resistors nominal that is the same or bigger than the calculated resistance.
 title
Figure 318: Raspberry Pi and LED control schematic.

The example of the blinking LED code:

#Raspberry Pi Python sample code
 
import RPi.GPIO as GPIO
import time
 
LED = 18    #GPIO04 port
 
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(LED,GPIO.OUT)
print ("LED on")
GPIO.output(LED,GPIO.HIGH)
time.sleep(1)
print ("LED off")
GPIO.output(LED,GPIO.LOW)

Displays

Using display is a quick way to get a feedback information from the device. There are many display technologies compatible with Arduino. For IoT solutions low power, easy to use and monochrome displays are used:

  • Liquid-crystal display (LCD),
  • Organic light-emitting diode display (OLED),
  • Electronic ink display (E ink).
Liquid-Crystal Display (LCD)

LCD uses modulating properties of liquid crystal light to block the incoming light. Thus when a voltage is applied to a pixel, it has a dark colour. A display consists of layers of electrodes, polarising filters, liquid crystals and reflector or back-light. Liquid crystals do not emit the light directly; they do it through reflection or backlight. Because of this reason, they are more energy efficient. Small, monochrome LCDs are widely used in devices to show a little numerical or textual information like temperature, time, device status etc. LCD modules commonly come with an onboard control circuit and are controlled through parallel or serial interface.

Figure 319: 16×2 LCD display [198].
Figure 320: Raspberry Pi and LCD screen schematics.

The example code:

#Raspberry Pi Python sample code
#!/usr/bin/python
#Example using a character LCD connected to a Raspberry Pi
 
import time
import Adafruit_CharLCD as LCD
 
#Raspberry Pi pin setup
lcd_rs = 25
lcd_en = 24
lcd_d4 = 23
lcd_d5 = 17
lcd_d6 = 18
lcd_d7 = 22
lcd_backlight = 2
 
#Define LCD column and row size for 16x2 LCD.
lcd_columns = 16
lcd_rows = 2
 
lcd = LCD.Adafruit_CharLCD(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, 
                     lcd_d7, lcd_columns, lcd_rows, lcd_backlight)
 
lcd.message('Hello\nworld!')
#Wait 5 seconds
 
time.sleep(5.0)
lcd.clear()
text = raw_input("Type Something to be displayed: ")
lcd.message(text)
 
#Wait 5 seconds
time.sleep(5.0)
lcd.clear()
lcd.message('Goodbye\nWorld!')
 
time.sleep(5.0)
lcd.clear()
Organic Light-Emitting Diode Display (OLED)

OLED display uses electroluminescent materials that emit light when the current passes through these materials. The display consists of two electrodes and a layer of an organic compound. OLED displays are thinner than LCDs, they have higher contrast, and they can be more energy efficient depending on usage. OLED displays are commonly used in mobile devices like smartwatches, cell phones and they are replacing LCDs in other devices. Small OLED display modules usually have an onboard control circuit that uses digital interfaces like I2C or SPI.

 title
Figure 321: OLED I2C display [199].
 title
Figure 322: Raspberry Pi and OLED I2C schematics.
1. git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git
2. cd Adafruit_Python_SSD1306
For Python 2:
3. sudo python setup.py install
For Python3:
4. sudo python3 setup.py install
cd examples
Choose one of existing examples:
  - animate.py
  - buttons.py
  - image.py
  - shapes.py
  - stats.py
Electronic Ink Display (E-ink)

E-ink display uses charged particles to create a paper-like effect. The display consists of transparent microcapsules filled with oppositely charged white and black particles between electrodes. Charged particles change their location, depending on the orientation of the electric field, thus individual pixels can be either black or white. The image does not need the power to persist on the screen; power is used only when the image is changed. Thus e-ink display is very energy efficient. It has high contrast and viewing angle, but it has a low refresh rate. E-ink displays are commonly used in e-riders, smartwatches, outdoor signs, electronic shelf labels.

 title
Figure 323: E ink display module [200].
 title
Figure 324: Raspberry Pi and E ink display module schematics.
#Raspberry Pi Python sample code
#From https://www.instructables.com/id/Waveshare-EPaper-and-a-RaspberryPi
 
import time, datetime, sys, signal, urllib, requests
from EPD_driver import EPD_driver
def handler(signum, frame):
    print ('SIGTERM')
    sys.exit(0)
signal.signal(signal.SIGTERM, handler)
bus = 0 
device = 0
disp = EPD_driver(spi = SPI.SpiDev(bus, device))
print ("disp size : %dx%d"%(disp.xDot, disp.yDot))
print ('------------init and Clear full screen------------')
disp.Dis_Clear_full()
disp.delay()
#Display part
disp.EPD_init_Part()
disp.delay()
imagenames = [] 
search = "http://api.duckduckgo.com/?q=Cat&format=json&pretty=1"
if search:
  req = requests.get(search)
  if req.status_code == 200:
    for topic in req.json()["RelatedTopics"]:
      if "Topics" in topic:
        for topic2 in topic["Topics"]:
          try:
            url = topic2["Icon"]["URL"]
            text = topic2["Text"]
            if url:
              imagenames.append( (url,text) )
            except:
              #Print topic
              pass
            try:
              url = topic["Icon"]["URL"]
              if url:
                imagenames.append( url )
            except:
              #Print topic
              pass
    else:
      print (req.status_code)
#Font for drawing within PIL
myfont10 = ImageFont.truetype("amiga_forever/amiga4ever.ttf", 8)
myfont28 = ImageFont.truetype("amiga_forever/amiga4ever.ttf", 28)
#Mainimg is used as screen buffer, all image composing/drawing is done in PIL,
#The mainimg is then copied to the display (drawing on the disp itself is no fun)
mainimg = Image.new("1", (296,128))
name = ("images/downloaded.png", "bla")
skip = 0
while 1:
  for name2 in imagenames:
    print ('---------------------')
    skip = (skip+1)%7
    try:
      starttime = time.time()
      if skip==0 and name2[0].startswith("http"):
        name = name2
        urllib.urlretrieve(name[0], "images/downloaded.png")
        name = ("images/downloaded.png", name2[1])
        im = Image.open(name[0])
        print (name, im.format, im.size, im.mode)
        im.thumbnail((296,128))
        im = im.convert("1") #, dither=Image.NONE)
        #Print ('thumbnail', im.format, im.size, im.mode)
        loadtime = time.time()
        print ('t:load+resize:', (loadtime - starttime))
        draw = ImageDraw.Draw(mainimg)
        #Clear
        draw.rectangle([0,0,296,128], fill=255)
        #Copy to mainimg
        ypos = (disp.xDot - im.size[1])/2
        xpos = (disp.yDot - im.size[0])/2
        print ('ypos:', ypos, 'xpos:', xpos)
        mainimg.paste(im, (xpos,ypos))
        #Draw info text
        ts = draw.textsize(name[1], font=myfont10)
        tsy = ts[1]+1
        oldy = -1
        divs = ts[0]/250
        for y in range(0, divs):
          newtext = name[1][(oldy+1)*len(name[1])/divs:(y+1)*len(name[1])/divs]
          #Print (divs, oldy, y, newtext)
          oldy = y
          draw.text((1, 1+y*tsy), newtext, fill=255, font=myfont10)
          draw.text((1, 3+y*tsy), newtext, fill=255, font=myfont10)
          draw.text((3, 3+y*tsy), newtext, fill=255, font=myfont10)
          draw.text((3, 1+y*tsy), newtext, fill=255, font=myfont10)
          draw.text((2, 2+y*tsy), newtext, fill=0, font=myfont10)
          #Draw time
          now = datetime.datetime.now()
          tstr = "%02d:%02d:%02d"%(now.hour,now.minute,now.second)
          #Draw a shadow, time
          tpx = 36
          tpy = 96
          for i in range(tpy-4, tpy+32, 2):
            draw.line([0, i, 295, i], fill=255)
            draw.text((tpx-1, tpy  ), tstr, fill=0, font=myfont28)
            draw.text((tpx-1, tpy-1), tstr, fill=0, font=myfont28)
            draw.text((tpx  , tpy-1), tstr, fill=0, font=myfont28)
            draw.text((tpx+2, tpy  ), tstr, fill=0, font=myfont28)
            draw.text((tpx+2, tpy+2), tstr, fill=0, font=myfont28)
            draw.text((tpx  , tpy+2), tstr, fill=0, font=myfont28)
            draw.text((tpx  , tpy  ), tstr, fill=255, font=myfont28)
            del draw
            im = mainimg.transpose(Image.ROTATE_90)
            drawtime = time.time()
            print ('t:draw:', (drawtime - loadtime))
            listim = list(im.getdata())
            #Print (im.format, im.size, im.mode, len(listim))
            listim2 = []
            for y in range(0, im.size[1]):
              for x in range(0, im.size[0]/8):
                val = 0
                for x8 in range(0, 8):
                  if listim[(im.size[1]-y-1)*im.size[0] + x*8 + (7-x8)] > 128:
                    #Print (x,y,x8,'ON')
                    val = val | 0x01 << x8
                  else:
                    #Print (x,y,x8,'OFF')
                    pass
                    #Print val
                    listim2.append(val)
            for x in range(0,1000):
             listim2.append(0)
            #Print len(listim2)
            convtime = time.time()
            print ('t:conv:', (convtime - loadtime))
            ypos = 0
            xpos = 0
            disp.EPD_Dis_Part(xpos, xpos+im.size[0]-1, ypos, 
            ypos+im.size[1]-1, listim2) #xStart, xEnd, yStart, yEnd, DisBuffer
            #disp.delay()
            uploadtime = time.time()
            print ('t:upload:', (uploadtime - loadtime))
        except IOError as ex:
          print ('IOError', str(ex))

Mechanical Drivers

Relay

Relays are electromechanical devices that use electromagnets to connect or disconnect plates of a switch. Relays are used to control high power circuits with low power circuits. Circuits are mechanically isolated and thus protect logic control. Relays are used in household appliance automation, lighting and climate control.

 title
Figure 325: 1 channel relay module [201].
 title
Figure 326: Raspberry Pi and 1 channel relay module schematics.

The example code:

#Raspberry Pi Python sample code
 
import RPi.GPIO as GPIO
import time
 
REL = 18    #GPIO04 port
 
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(REL,GPIO.OUT)
print ("REL on")
GPIO.output(REL,GPIO.HIGH)
time.sleep(1)
print ("REL off")
GPIO.output(REL,GPIO.LOW)
Solenoid

Solenoids are devices that use electromagnets to pull or push iron or steel core. They are used as linear actuators for locking mechanisms indoors, pneumatic and hydraulic valves and in-car starter systems.

Solenoids and relays both use electromagnets and connecting them to Arduino is very similar. Coils need a lot of power, and they are usually attached to the power source of the circuit. Turning the power of the coil off makes the electromagnetic field to collapse and creates very high voltage. For the semiconductor devices protection, a shunt diode is used to channel the overvoltage. For extra safety, optoisolator can be used.

 title
Figure 327: Long-stroke latching solenoid [202].
 title
Figure 328: Raspberry Pi and solenoid schematics.

The example code:

#Raspberry Pi Python sample code
 
import RPi.GPIO as GPIO
import time
 
SOL = 18    #GPIO04 port
 
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(SOL,GPIO.OUT)
print ("SOL on")
GPIO.output(SOL,GPIO.HIGH)
time.sleep(1)
print ("SOL off")
GPIO.output(SOL,GPIO.LOW)
DC Motor (One Direction)

An electric motor is an electro-technical device which can turn electrical energy into mechanical energy; motor turns because of the electricity that flows in its winding. Electric motors have seen many technical solutions over the year from which the simplest is the permanent-magnet DC motor.

DC motor is a device which converts direct current into the mechanical rotation. DC motor consists of permanent magnets in stator and coils in the rotor. By applying the current to coils, the electromagnetic field is created, and the rotor tries to align itself to the magnetic field. Each coil is connected to a commutator, which in turns supplies coils with current, thus ensuring continuous rotation. DC motors are widely used in power tools, toys, electric cars, robots, etc.

 title
Figure 329: A DC motor [203].
 title
Figure 330: Raspberry Pi and DC motor schematics.
#Raspberry Pi Python sample code
 
import RPi.GPIO as GPIO
import time
 
DCM = 18    #GPIO04 port
 
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(DCM,GPIO.OUT)
print ("DCM on")
GPIO.output(DCM,GPIO.HIGH)
time.sleep(1)
print ("DCM off")
GPIO.output(DCM,GPIO.LOW)
Stepper Motor

Stepper motors are motors, that can be moved by a certain angle or step. Full rotation of the motor is divided into small, equal steps. Stepper motor has many individually controlled electromagnets, by turning them on or off, the motor shaft rotates by one step. Changing switching speed or direction can precisely control turn angle, direction or full rotation speed. Because of very precise control ability they are used in CNC machines, 3D printers, scanners, hard drives etc. Example of use can be found in the source [204].

 title
Figure 331: A stepper motor [205].
 title
Figure 332: Raspberry Pi and stepper motor schematics.

The example code:

#Raspberry Pi Python sample code
#From https://www.rototron.info/raspberry-pi-stepper-motor-tutorial/
 
from time import sleep
import RPi.GPIO as GPIO
 
DIR = 20   #Direction GPIO Pin
STEP = 21  #Step GPIO Pin
CW = 1     #Clockwise Rotation
CCW = 0    #Counterclockwise Rotation
SPR = 48   #Steps per Revolution (360/7.5)
 
GPIO.setmode(GPIO.BCM)
GPIO.setup(DIR, GPIO.OUT)
GPIO.setup(STEP, GPIO.OUT)
GPIO.output(DIR, CW)
 
step_count = SPR
delay = .0208
 
for x in range(step_count):
  GPIO.output(STEP, GPIO.HIGH)
  sleep(delay)
  GPIO.output(STEP, GPIO.LOW)
  sleep(delay)
 
sleep(.5)
GPIO.output(DIR, CCW)
for x in range(step_count):
 GPIO.output(STEP, GPIO.HIGH)
 sleep(delay)
 GPIO.output(STEP, GPIO.LOW)
 sleep(delay)
 
GPIO.cleanup()

This code may result in motor vibration and jerky motion especially at low speeds. One way to counter these result is with microstepping. Adding the code above avoid it:

MODE = (14, 15, 18)   #Microstep Resolution GPIO Pins
GPIO.setup(MODE, GPIO.OUT)
RESOLUTION = {'Full': (0, 0, 0),
              'Half': (1, 0, 0),
              '1/4': (0, 1, 0),
              '1/8': (1, 1, 0),
              '1/16': (0, 0, 1),
              '1/32': (1, 0, 1)}
GPIO.output(MODE, RESOLUTION['1/32'])

step_count = SPR * 32
delay = .0208 / 32

Raspberry Pi OS Guide

Supported Operating Systems (OS)

Raspberry Pi all models are based on ARM processors which are typically quad-core Cortex-A7 CPUs. This means that most of popular multitasking OS systems can be uploaded and used to create and develop user software operations. The list of supported OS systems contains Linux, Windows and thirty part OS systems. The following list of figures of OS are specially designed for Raspberry Pi boards:

Figure 333: Raspbian [206].
Figure 334: Ubuntu Mate [207].
Figure 335: Snappy Ubuntu Core [208].
Figure 336: Windows 10 IOT Core [209].
Figure 337: OSMC [210].
Figure 338: LibreELEC [211].
Figure 339: Pinet [212].
Figure 340: Risc OS [213].

Before start installing the OS system on the Raspberry Pi board developer must prepare his hardware for it [214]. It means that the minimum hardware equipment is needed:

  • Raspberry Pi board,
  • monitor or TV with HDMI port,
  • HDMI cable,
  • USB keyboard,
  • USB mouse,
  • power supply,
  • at least 8 GB micro SD card (C10 class is welcome).

Connecting all establishes the minimum PC desktop kit which will allow to install and run the selected OS system on the SD card.

Downloading OS System

There are few ways to get the right OS system for Raspberry Pi board:

  • buy pre-installed SD card from RS [215] or PiHut [216],
  • install Raspbian with NOOBS [217],
  • download Raspbian image directly from Raspberry Pi software repository [218],
  • download the Windows 10 IOT OS from Microsoft Windows Insider Preview [219].

Other Raspberry OS Systems

For other then Windows and NOOBS systems use the Etcher SD [220] card image utility which is designed to format and upload to the SD card different operating systems images. Then follow its instructions.

Figure 341: Etcher SD card image utility view.

Programming Fundamentals Raspbian OS

Installing the Raspbian OS

To install the OS system on SD card the best way is to use specially designed software which will provide SD card formatting tool.

Step 1

Download and install the SD Formatter [221] tool. Run the SD Formatter tool.

Figure 342: SD Formatter view.
Step 2

Insert the SD card into the computer SD card reader.

Step 3

Run the SD Formatter, select the drive letter for the SD card and format it.

Step 4

Simply drag and drop the extracted NOOBS OS image files from unzipped NOOBS folder onto the SD card drive. The necessary files will be transferred to the SD card.

Step 5

Gently remove SD card from the reader and push it into the Raspberry Pi SD card slot.

Step 6

Power on the Raspberry Pi board and follow its instructions.

After the board reboot the Raspbian screen displays.

Figure 343: Raspbian desktop view.

Raspberry Pi Python Programming Guide

Python Language

Python belongs to the high-level programming languages class which was first time developed by Guido van Rossum in 1991. The Python is similar to C++, C# or Java programming languages. It is very useable with a clean syntax and easy to learn even for programming beginners.

Raspberry Raspbian OS is shipped with pre installed two versions of Python language: Python2 and Python3 which are available from the Raspbian Menu.

Figure 344: Python menu view.

Choosing the Python version from the menu, the command window with cursor opens.

Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 20 2018, 15:12:02) 
[MSC v.1900 64 bit (AMD64)] on Win32
Type "copyright", "credits" or "license()" for more information.
>>>

To test the simply program ”Hello World!“ User just can entry:

Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 20 2018, 15:12:02) 
[MSC v.1900 64 bit (AMD64)] on Win32
Type "copyright", "credits" or "license()" for more information.
>>>printf("Hello World")
Hello World!
>>>

Writing the same code in C language will look following:

#include <stdio.h>
int main()
{
  printf ("Hello World!");
  return 0;
}
Python Program Features

Python can automate tasks using the batch commands renaming and move large amounts of files like shell scripts. It can be used as a command line with IDLE, Python’s REPL (read, eval, print, loop) functions. However, there are more useful tasks which can be done with Python. For example, Python can be used to program things like:

  • web applications,
  • desktop applications and utilities,
  • special GUIs,
  • small databases,
  • 2D games.

Because Python stays very popular, it has a large collection of libraries, which speeds up the development process. There exist libraries for – game programming, rendering graphics, GUI interfaces, web frameworks, and scientific computing.

Many of the programmings stuff in C language can also be programmed in Python. Python is generally slower at computations regarding the C compiler, but its ease for use, which makes Python a very popular tool for prototyping programs and designing applications which are not computationally intensive. One of the best Python tutorials can be found in the book: “Learning Python, 5th Edition by Mark Lutz”.

Installing and Updating Python

Python 2 and Python 3 come pre-installed on Raspbian OS systems, but if necessary to install Python on another Linux OS or to update it, the simple commands can be executed at the command prompt:

sudo apt-get install python3

Installs or updates Python 3.

sudo apt-get install python

Installs or updates Python 2.

Opening the PYTHON REPL

To access the Python REPL (where the user can type Python commands just like the command line) the user can enter python or python3 commands depending on which version of Raspbian to use in the command prompt.

Figure 345: Python REPL view.

Pressing (CTRL-D) exits the REPL.

Running a Python Program

To run the program without making it executable, the user must navigate to the location where the file exists and enter the command:

python hello-world.py
Make Python File Executable

Making a Python program executable allows to run the program without entering python before the file name. User can make a file executable executing the following commands in the command prompt:

chmod +x file-name.py

Now to run the program:

./file-name.py
Additional Resources for Python programming
  1. The Python syntax and semantics: Python Semantics.
  2. The Python Package Index (PyPi): PyPi.
  3. The Python Standard Library:PSL.

[pczekalski]Add a simple app in Python. Check if Micropython has printing support over serial for tracing

Python Data Types and Variables

Python aims to be consistent and straightforward in the design of its syntax. The best advantage of this language is that it can dynamically set the variable types depending on values types which are set for variables.

Base Types

Python has a wide range of data types, like many simple programming languages:

  • number,
  • string,
  • list,
  • tuple
  • dictionary.
Numbers

Standard Python methods are used to create the numbers:

var = 1234     #Creates Integer number assignment
var = 'George' #Creates String type var

Python can automatically convert types of the number from one type to another. Type can be also defined explicitly.

int a = 10
long a = 123L
float a = 12.34
complex a = 3.23J
<code>
 
==String==
To define Strings use eclosing characters in quotes. 
Python uses single quotes ', double " and triple """ to denote strings.
<code Python>
Name = "George'
lastName = "Smith"
message = """this is the string message which is spanning across multiple lines."""
List

List contains a series of values. To declare list variables uses brackets [].

A = [] #Blank list variable
B = [1, 2, 3] #List with 3 numbers
C = [1, 'aa', 3] #List with different types

List are zero-based indexed. Data can be assigned to a specific element of the list using an index into the list.

mylist[0] = 'sasa'
mylist[1] = 'wawa'
 
print mylist[1]

List aren't limited to a single dimension.

myTable = [[],[]]

In two-dimensional array the first number is always the rows number, when the second is the columns number.

Tuple

Python Tuples are defined as a group of values like a list and can be processed in similar ways. When assigned Tuples got the fixed size. In Python, the fixed size is immutable. The lists are dynamic and mutable. To define Tuples, parenthesis () must be used.

TestSet = ('Piotr', 'Jan', 'Adam')
Dictionary

To define the Dictionaries in the Python the lists of key–value pairs are used. This datatype is used to hold related information that can be associated through Keys. The Dictionary is used to extract a value based on the key name. Lists use the index numbers to access its members when dictionaries use a key. Dictionaries generally are used to sort, iterate and compare data.

To define the Dictionaries the braces ({}) are used with pairs separated by a comma (,) and the key values associated with a colon (:). Dictionaries Keys must be unique.

box_nbr = {'Alan': 111, 'John': 222}
box_nbr['Alan'] = 222     #Set the associated 'Alan' key to value 222'
print (box nbr['John'])   #Print the 'John' key value
box_nbr['Dave'] = 111     #Add a new key 'Dave' with value 111
print (box_nbr.keys())    #Print the keys list in the dictionary
print ('John' in box_nbr) #Check if 'John' is in the dictionary
                          #This returns true

All variables in Python hold references to objects, and are passed to functions. Function can't change the value of variable references in its body. The object's value may be changed in the called function with the “alias”.

>>> alist = ['a', 'b', 'c']
>>> def myfunc(al):
    al.append('x')
    print al

>>> myfunc(alist)
['a', 'b', 'c', 'x']
>>> alist
['a', 'b', 'c', 'x']

Python Program Control Structures

if Statements

If an expression returns TRUE statements are carried out. Otherwise they aren't.

if expression:
  statements

Sample:

no = 11
  if no >10:
   print ("Greater than 10")
   if no <=30
     printf ("Between 10 and 30")

Output:

>>>
Greater than 10
Between 10 and 30
>>>
else Statements

An else statement follows an if statement and contains code that is called when the if statement is FALSE.

x = 2
if x == 6
  printf ("Yes")
else:
  printf ("No")  
elif Statements

The elif (shortcut of else if) statement is used when changing if and else statements. A series of if…elif statements can have a final else block, which is called if none of the if or elif expression is TRUE.

num = 12
if num == 5:
  printf ("Number = 5")
elif num == 4:
  printf ("Number = 4")
elif num == 3:
  printf ("Number = 3")
else:
  printf ("Number = 12")

Output:

>>>
 Number = 12
>>>
Boolean Logic

Python uses logic operators like AND, OR and NOT.

The AND operator uses two arguments, and evaluates to TRUE if, and only if, both of the arguments are TRUE. Otherwise, it evaluates to FALSE.

>>> 1 == 1 and 2 == 2
True
>>> 1 == 1 and 2 == 3
False
>>> 1 != 1 and 2 == 2
False
>>> 4 < 2 and 2 > 6
False
>>>

Boolean operator or uses two arguments, and evaluates as TRUE if either (or both) of its arguments are TRUE, and FALSE if both arguments are FALSE.

The result of NOT TRUE is FALSE, and NOT FALSE goes to TRUE.

>>> not 2 == 2
False
>>> not 6 > 10
True
>>>
Operator Precedence

Operator Precedence uses mathematical idea of operation order, e.g. multiplication begin performed before addition.

>>> False == False or True
True
>>> False == (False or True)
False
>>> (False == False) or True
>>>True
>>>

Python Looping

while Loop

An if statement is run once if its condition evaluates to TRUE, and never if it evaluates to FALSE.

A while statement is similar, except that it can be run more than once. The statements inside it are repeatedly executed, as long as the condition holds. Once it evaluates to FALSE, the next section of code is executed.

i = 1
while i<=4:
  print (i)
  i+=1
print ('End') 

Output:

>>>
1
2
3
4
End
>>>

The infinite loop is a particular kind of the while loop, it never stops running. Its condition always remains TRUE.

while 1 == 1:
 print ('in the loop')

To end the while loop prematurely, the break statement can be used. When encountered inside a loop, the break statement causes the loop to finish immediately.

i = 0
while 1==1:
  print (i)
  i += 1
  if i >=3:
    print('breaking')
    break;
print ('finished')    

Output:

>>>
0
1
2
3
breaking
finished
>>>

Another statement that can be used within loops is continue.

Unlike break, continue jumps back to the top of the loop, rather than stopping it.

i = 0
while True:
  i+=1
  if i == 2:
    printf ('skipping 2')
    continue
  if i == 5:
    print ('breaking')
    break
  print (i)
print ('finished')  

Output:

>>>
1
skipping 2
3
4
breaking
finished
>>>
for Loop
n = 9
for i in range (1,5):
  ml = n * i
  print ("{} * {} = {}".format (n, i, ml))

Output:

>>>
9 * 1 = 9
9 * 2 = 18
9 * 3 = 27
9 * 4 = 36
>>>

Python Sub-Programs

Subprograms

One of the most important in mathematics concept is to use functions. Functions in computer languages implement mathematical functions. The executing function produces one or more results, which are dependent by the parameters passed to it.

In general, a function is a structuring element in the programming language which groups a set of statements so they can be called more than once in a program. Programming without functions will need to reuse code by copying it and changing its different context. Using functions enhances the comprehensibility and quality of the program. It also lowers the memory usage, development cost and maintenance of the software.

Different naming is used for functions in programming languages, e.g. as subroutines, procedures or methods.

Python language defines function by a def statement. The function syntax looks:

def function-name(Parameter list):
  statements, i.e. the function body

Function bodie can contain one or more return statement. It can be situated anywhere in the function body. A return statement ends the function execution and returns the result, i.e. to the caller. If the return statement does not contain expression, the value None is returned.

def Fahrenheit(T_in_celsius):
  """ returns the temperature in degrees Fahrenheit """
  return (T_in_celsius * 9 / 5) + 32
 
for t in (22.6, 25.8, 27.3, 29.8):
  print(t, ": ", fahrenheit(t))

Output:

>>>
22.6 :  72.68
25.8 :  78.44
27.3 :  81.14
29.8 :  85.64
>>>
Optional Parameters

Functions can be called with optional parameters, also named default parameters. If function is called without parameters the default values are used. The following code greets a person. If no person name is defined, it greets everybody:

def Hello(name="everybody"):
  """ Say hello to the person """
  print("Hello " + name + "!")
 
Hello("George")
Hello()

Output:

>>>
Hello George!
Hello everybody!
>>>
Docstrings

The string is usually the first statement in the function body, which can be accessed with function_name.doc. This is Docstring statement.

def Hello(name="everybody"):
  """ Say hello """
  print("Hello " + name + "!")
print("The docstring of the function Hello: " + Hello.__doc__)

Output:

>>>
The function Hello docstring:  Say hello
>>>
Keyword Parameters

The alternative way to make function calls is to use keyword parameters. The function definition stay unchanged.

def sumsub(a, b, c=0, d=0):
  return a - b + c - d
print(sumsub(12,4))
print(sumsub(42,15,d=10))

Only keyword parameters are valid, which are not used as positional arguments. If keyword parameters don't exist, the next call to the function will need all four arguments, even if the c needs just the default value:

print(sumsub(42,15,0,10))
Return Values

In above examples, the return statement exist in sumsub but not in Hello function. The return statement is not mandatory. If explicitly return statement doesn't exist in the sample code it will not show any result:

def no_return(x,y):
  c = x + y
res = no_return(4,5)
print(res)

Any result will not be displayed in:

>>>

Executing this script, the None will be printed. If a function doesn't contain expression the None will also be returned:

def empty_return(x,y):
    c = x + y
    return
res = empty_return(4,5)
print(res)

Otherwise the expression value following return will be returned. In this example 11 will be printed:

def return_sum(x,y):
  c = x + y
  return c
res = return_sum(6,5)
print(res)

Output:

>>>
9
>>>
Multiple Values Returning

Any function can return only one object. An object can be a numerical value – integer, float, list or a dictionary. To return i.e. three integer values, we can return a list or a tuple with these three integer values. It means that function can indirectly return multiple values. This following example calculates the Fibonacci boundary for a positive number, returns a 2-tuple. The Largest Fibonacci Number smaller than x is the first and the Smallest Fibonacci Number larger than x is next. The return value is stored via unpacking into the variables lub and sup:

def fib_intervall(x):
  """ returns the largest Fibonacci number, smaller than x and the lowest
  Fibonacci number, higher than x"""
  if x < 0:
    return -1
  (old, new, lub) = (0,1,0)
  while True:
    if new < x:
      lub = new 
      (old,new) = (new,old+new)
    else:
      return (lub, new)
 
while True:
  x = int(input("Your number: "))
  if x <= 0:
    break
  (lub, sup) = fib_intervall(x)
  print("Largest Fibonacci Number < than x: " + str(lub))
  print("Smallest Fibonacci Number > than x: " + str(sup))

Programming Fundamentals Windows 10 IOT Core

Installing the Windows 10 IOT Core

Microsoft Windows 10 IOT OS system is available for download from Windows Insider Preview Downloads page [222].

Step 1

Register into the Microsoft Insider Program. To download images of the Microsoft IOT Core user must be logged into Microsoft Insider Program web page.

Step 2

On the download page, User must choose which OS edition he wants to use in his project ieg. Windows 10 IOT Core Insider preview – build 17083 or 17035. The core numbers are changing depending on the latest developer editions available in the Microsoft repository. Microsoft IOT development policy is straightly tied with the latest Visual Studio environment. To fully use its power, users are asked to use the latest Visual Studio and Windows 10 IOT core builds in the development process synchronously. The Windows 10 IOT Core platform is still under development and improvement.

Figure 346: Windows Insider Program Win10 Core download page.

Step 3

Install the right Windows10_InsiderPreview_IoTCore_RPi_ARM32_en-us_17035.iso image on your Windows 10 PC. This package installs the Windows IOT Core Image Helper application and stores the latest Raspberry Pi Windows 10 core image flash.ffu file into the C:\Program Files (x86)\Microsoft IoT\ directory.

Step 4

Insert the SD card to your computer SD cards reader slot.

Step 5

Run the Windows IOT Core Image Helper and follow its instructions – select the SD card drive letter, select the right FFU image in the C:\Program Files (x86)\Microsoft IoT\FFU\RaspberryPi2 folder.

Figure 347: Windows IOT Core Helper view.

Step 6

Start formatting the SD card and install the FFU image on it.

Step 7

Gently remove SD card from the reader and push it into the Raspberry Pi SD card slot.

Step 8

Power on the Raspberry Pi board and follow the Windows 10 Core setup commands configuring the Windows 10 Core features.

After the board reboot the Main Windows 10 Core screen displays:

Figure 348: Windows 10 Core system view.

Raspberry Pi Programming Guide

This chapter describes the typical programming technics used in Raspberry Pi boards developing projects.

Raspberry Pi Under Windows 10 IoT Core

To create and develop control applications on the Raspberry Pi boards needs the following development environment:

  • PC with Windows 10 System installed,
  • Visual Studio 2015 or higher,
  • Raspberry PI 2 or 3 board with Windows 10 IoT Core installed,
  • configured TCP/IP network for Raspberry Pi and Windows 10 Desktop computer (Local LAN or WiFi subnet),

Programming skills needed:

  • C# language knowledge,
  • XML/XAML language knowledge,
  • Windows API understanding.

For better development Raspberry applications, the Windows IoT Remote Client is welcome. This application is available for download from Microsoft Store. This application captures keyboard, mouse and screen from the Raspberry Pi board running the Windows 10 IoT Core system on the desktop PC. It allows developers to use standalone Raspberry board without a connected mouse, keyboard and monitor to it.

Figure 349: Microsoft Store – Windows IoT Remote Client.

To write and develop applications under Windows 10 IoT Core developer must possess basic knowledge of how Windows operating system interacts with User applications. The major advantage of using Windows 10 IoT Core is that Microsoft concept based on use the same Kernel API available on different hardware platforms – desktop PCs, IoT boards suitable to run Windows Core, Tablets, phones etc. It reduces development costs due to the unifying system environment, and the only difference is in display view of the same application code written in C#/C++. Windows 10 Core is specially designed to handle applications working as standalone on the IoT platforms in 24/7 time model.

Configuring the Windows 10 IoT Core Platform

After installing the Windows 10 IoT Core, the developer must configure IoT platform using Windows Device Portal (WDP).

Figure 350: Windows Device Portal view.

IoT board can be managed using any Internet browser – Chrome, Microsoft Edge, Firefox etc. To open the WDB portal on the IoT board user must enter the board IP address – IPaddress:8080/default.htm. The site is protected with username/password. Default account credentials are: administrator/p@ssw0rd. Following tabs in the WDP, it is possible to configure all necessary IoT platform settings, check the current board status, download development crash/debug information, configure network/Bluetooth settings, download drivers and configure security TPM modules. If all tasks are ready, the developer can start to write his own IoT application under Windows 10 IoT Core. Following steps are recommended before IoT board will be used for application:

Step 1

In the Device Settings user is recommended to Change your device name. The default name is minwinpc. The aim to change it is that if a user uses many of the IoT devices connected to the same network segment, it is difficult to recognise which role each device is set for. Enumerating IoT devices will show boards with the same name but with different IP addresses. Proper naming will make it easy to know what role each device plays.

Step 2

Because RPI boards don't have their own RTC clock modules, Windows 10 IoT Core sets the time using the NTP services during its work. So very important in the industrial implementations and in a case when the time is important in developed applications is to set the proper time zone for the board. In the Device settings user is recommended to select proper Time zone

Step 3

Security reasons – the default password for the newly flashed device is p@ssw0rd. It is strongly recommended right after the first board boot to change it – to set it unique! It will prevent the IoT device from remote hacking. The password can be changed in Device Settings tab.

Step 4

The Windows 10 IoT Core comes with Cortana service ready. If the board is equipped with microphone and speakers, it is always possible to turn the Cortana service on for voice commands communication with the board.

Step 5

If the IoT board needs special hardware connected to it then in the Devices/Device Manager user can upload and install appropriate driver for it in case if it is not preinstalled in the IoT Core.

Figure 351: Device Manager view.
Step 6

Raspberry Pi boards 1/2/3 are equipped with network connection modules. In case if the board under Windows 10 IoT Core is connected to LAN RJ45 connector the IP number can be set via DHCP server. In case if user wants to use WiFi connection or to activate Bluetooth then he can do it directly on the board main display or manage them via Windows Device Portal.

Figure 352: Network & WiFi view.
Figure 353: Network view.
Step 7

Security. In a case when IoT device must be protected for remote hacking one of solutions is to use Trusted Platform Modules (TPM) module following ISO/IEC 11889 standards for a secure cryptoprocessor, a dedicated microcontroller designed to secure hardware through integrated cryptographic keys. The chip contains physical security mechanisms to protect it from tampering, and malicious software is unable to hack the TPM security functions. Some of the TPM key advantages are:

  • generate and store the cryptographic keys;
  • use the TPM unique RSA key technology for platform device authentication, which is burned into the chip;
  • help ensure platform integrity.

The most common TPM functions are used for system integrity measurements, key creation and use. During the system boot process, the boot code is loaded (including firmware and OS components) and can be measured and recorded in the TPM module. The integrity measurements are used for evidence how OS started and to be sure when correct boot software was used with TPM-based key. Windows 10 IoT Core supports few TPM modules standards which can be connected to the 40-pin GPIO connector.

Figure 354: TPM module view.

Under the TPM Configuration tab in the Windows Device, Portal user can select the right communication protocol for the TPM module installed in the Raspberry Pi board. Then appropriate driver for the TPM module can be installed in the Device Manager tab.

Figure 355: TPM Configuration view.

RPI Windows 10 IoT Sample Project

Create Simple Hello World Application for Raspberry Pi Board

To create simple Hello Word application under Windows 10 IoT Core the Visual Studio 2015 or higher is needed. Visual Studio must be installed with the Universal Windows Platform development extension.

Figure 356: Visual Studio UWP development view.
Step 1

Create new UWP project choosing the Windows Universal/Blank App Project.

Figure 357: Create new UWP App.
Step 2

Select target version (according to Raspberry Pi Windows 10 IoT Core build version)

Figure 358: Select target version.
Step 3

Create Hello solution.

Figure 359: Select target version.
Step 4

Design the application screen modifying the MainPage.xaml file. To add different screen features use the Toolbox/All XAML controls.

Figure 360: Add TextBlock control.
Step 5

Modify the MainPage.cs file content if you need controls events programming.

Figure 361: Add TextBlock control.
Step 6

Compile and run Hello solution. Choosing the Solution Platform to the x86 user will be able to debug and run Hello application on the computers desktop emulator. This step is useful for program touchscreen design but is not capable of testing the sensors and controls programming due to software emulator restrictions.

Figure 362: Run Hello solution.

Software emulators aren't capable of simulating their behaviour. To use sensors and controls instead, the Solution Platform must be changed to the ARM platform in the VC Solution Configuration property. To debug it application package must be then transferred to the real IoT device.

Figure 363: Select hardware platform for the solution.
Step 7

To deploy and debug the application package on the real IoT device user must configure the debug application settings. In the Debug property page user must enter the proper Remote IoT device IP number.

Figure 364: Set the Remote machine IP number.
Step 8

Start debugging application and after deploy application package to the board SD card it will be displayed on the monitor.

Figure 365: Hello Application on the Raspberry Pi display.

C# Variables and Data Types

The C# [223] variables are categorized into the following types:

  • value types,
  • reference types,
  • pointer types.
Value Type Variables

Value type variables can assign a value directly. The class System.ValueType defines them.

The value types directly contain data. Value types may be: int, char and float, storing numbers, strings or floating point values. When an int type is declared, the system allocates memory to store its value.

The available value types list in C# is presented following:

Table 25: C# 2010 Value Definitions
Type Represents Range Default Value
bool Boolean value True or False False
byte 8-bit unsigned integer 0 to 255 0
char 16-bit Unicode character U +0000 to U +ffff '\0'
decimal 128-bit precise decimal values with 28-29 significant digits (–7.9 × 10E28 to 7.9 × 10E28) / 10E0 to 28 0.0M
double 64-bit double-precision floating point type (+/–)5.0 × 10E–324 to (+/–)1.7 × 10E308 0.0D
float 32-bit single-precision floating point type –3.4 × 10E38 to + 3.4 × 10E38 0.0F
int 32-bit signed integer type –2 147 483 648 to 2 147 483 647 0
long 64-bit signed integer type –9 223 372 036 854 775 808 to 9 223 372 036 854 775 807 0L
sbyte 8-bit signed integer type –128 to 127 0
short 16-bit signed integer type –32 768 to 32 767 0
uint 32-bit unsigned integer type 0 to 4 294 967 295 0
ulong 64-bit unsigned integer type 0 to 18 446 744 073 709 551 615 0
ushort 16-bit unsigned integer type 0 to 65 535 0
Reference Type

The reference types don't contain the actual data stored in a variable. They contain a reference to the variables.

Using multiple variables, the reference types can refer to a memory location. If the variable changes the data in the memory location, the other variable automatically reflects this change in value. Built-in reference example types are: object, dynamic, and string.

Object Type

The Object Type is an alias for the System.Object class. It is the ultimate base class for all data types in C# Common Type System (CTS). The object types can be assigned with values of any other types, value types, reference types, predefined or user-defined types. Before assigning values, the type conversion is needed.

When a value type is converted to an object type, it is called boxing and when an object type is converted to a value type, it is called unboxing.

object obj;
obj = 100; //This is boxing
Dynamic Type

The data type variable can store any value. But this type checking takes place at run-time.

Syntax for declaring a dynamic type is:

dynamic <variable_name> = value;

For example,

dynamic d = 20;

Dynamic types are similar to object types. That type checking for object type variables takes place at compile time. For the dynamic type variables checking takes place at run time.

String Type

The string type allows assigning any string values to a variable. The string type is an alias for the System.String class and is derived from object type. The string type value can be assigned using string literals in two forms: quoted and @quoted.

For example,

string str = "Tutorials Point";

A @quoted string literal looks as follows:

@"Tutorials Point";

The user-defined reference types are: class, interface, or delegate.

Pointer Type

Pointer type variables store the memory address, which is another type. Pointers in C# are similar to pointers in C or C++.

Syntax for declaring a pointer type is:

type* identifier;

For example,

char* cptr;
int* iptr;

C# Variables

Each variable in C# has a specific type, which determines the size and layout of the variable's memory.

The basic value types in C# can be categorised as follows:

Table 26: C# 2010 Variables
Type Example
Integral types sbyte, byte, short, ushort, int, uint, long, ulong, and char
Floating point types float and double
Decimal types decimal
Boolean types true or false values, as assigned
Nullable types Nullable data types
Variable Definitions

Variable syntax definition in C# is:

<data_type> <variable_list>;

data_type must be a valid C# data type like char, int, float, double, or any user-defined data type. variable_list may consist of one or more identifier names separated by commas.

Examples of valid variable definitions are shown below:

int i, j, k;
char c, ch;
float f, salary;
double d;

Variable can be initialized immediately during definition time:

int i = 100;
Variables Initialization

Variables are initialized with an equal sign followed by a constant expression. The general initialization form looks:

variable_name = value;

Variables can be initialized during their declaration. The initializer consists of an equal sign followed by a constant expression as:

<data_type> <variable_name> = value;

Some examples are:

int d = 3, f = 5;    /* Initializing d and f */
byte z = 22;         /* Initializes z */
double pi = 3.14159; /* Declares an approximation of PI */
char x = 'x';        /* The variable x has the value 'x' */

It is important to initialize variables properly, otherwise sometimes it may produce unexpected result.

The following example uses various types of variables:

using System;
 
namespace VariableDefinition {
 class Program {
   static void Main(string[] args) {
     short a;
     int b ;
     double c;
     /* Actual initialization */
     a = 10;
     b = 20;
     c = a + b;
     Console.WriteLine("a = {0}, b = {1}, c = {2}", a, b, c);
     Console.ReadLine();
   }
 }
}

Output:

a = 10, b = 20, c = 30

C# Looping

C# [224] provides following types of loop to handle looping requirements.

Table 27: C# 2010 Loops Definitions
Sr.No. Loop Type & Description
1 while loop – it repeats a statement or a group of statements while a given condition is true. It tests the condition before executing the loop body
2 for loop – it executes a sequence of statements multiple times and abbreviates the code that manages the loop variable
3 do…while loop – it is similar to a while statement, except that it tests the condition at the end of the loop body
4 Nested loop – you can use one or more loop inside any another while, for or do…while loop
while Loop

A while loop statement in C# repeatedly executes a target statement as long as a given condition is true.

while(condition) {
   statement(s);
}

Example:

using System;
 
namespace Loops {
  class Program {
    static void Main(string[] args) {
      /* Local variable definition */
      int a = 10;
 
      /* while loop execution */
      while (a < 20) {
        Console.WriteLine("value of a: {0}", a);
        a++;
      }
      Console.ReadLine();
    }
  }
} 

Output:

value of a: 10
value of a: 11
value of a: 12
value of a: 13
value of a: 14
value of a: 15
value of a: 16
value of a: 17
value of a: 18
value of a: 19
for Loop

A for loop is a repetition control structure that allows you to efficiently write a loop that needs to execute a specific number of times. The syntax of a for loop in C# is:

for ( init; condition; increment ) {
  statement(s);
}

Here is the flow of control in a for loop.

  1. The init step is executed first, and only once. This step allows you to declare and initialise any loop control variables. You are not required to put a statement here, as long as a semicolon appears.
  2. Next, the condition is evaluated. If it is true, the body of the loop is executed. If it is false, the body of the loop does not run, and flow of control jumps to the next statement just after the for loop.
  3. After the body of the for loop executes, the flow of control jumps back up to the increment statement. This statement allows you to update any loop control variables. This statement can be left blank, as long as a semicolon appears after the condition.
  4. The condition is now evaluated again. If it is true, the loop executes, and the process repeats itself (body of the loop, then increment step, and then again testing for a condition). After the condition becomes false, the for loop terminates.

Example:

using System;
namespace Loops {
  class Program {
    static void Main(string[] args) {
 
      /* for loop execution */
      for (int a = 10; a < 20; a = a + 1) {
        Console.WriteLine("value of a: {0}", a);
      }
      Console.ReadLine();
    }
  }
} 

Output:

alue of a: 10
value of a: 11
value of a: 12
value of a: 13
value of a: 14
value of a: 15
value of a: 16
value of a: 17
value of a: 18
value of a: 19
The C# do...while Loop

The syntax of a do…while loop in C# is:

do {
  statement(s);
} while( condition );

Notice that the conditional expression appears at the end of the loop, so the statement(s) in the loop execute once before the condition is tested.

If the condition is true, the flow of control jumps back up to do, and the statement(s) in the loop execute again. This process repeats until the given condition becomes false. Example:

using System;
 
namespace Loops {
  class Program {
    static void Main(string[] args) {
      /* Local variable definition */
      int a = 10;
 
      /* do loop execution */
      do {
        Console.WriteLine("value of a: {0}", a);
        a = a + 1;
      } 
      while (a < 20);
      Console.ReadLine();
    }
  }
} 

Output:

value of a: 10
value of a: 11
value of a: 12
value of a: 13
value of a: 14
value of a: 15
value of a: 16
value of a: 17
value of a: 18
value of a: 19
C# Nested for Loop

C# allows using one loop inside another loop (loop nesting). The following section shows a few examples to illustrate the concept. The syntax for a nested for loop statement in C# is as follows:

for ( init; condition; increment ) {
  for ( init; condition; increment ) {
    statement(s);
  }
  statement(s);
}

The syntax for a nested while loop statement in C# is as follows:

while(condition) {
  while(condition) {
    statement(s);
  }
  statement(s);
}

The syntax for a nested do…while loop statement in C# is as follows:

do {
  statement(s);
  do {
    statement(s);
  }
  while( condition );
}
while( condition );

A final note on loop nesting is that you can put any type of loop inside of any other type of loop. For example, a for loop can be inside a while loop or vice versa. Example:

using System;
 
namespace Loops {
  class Program {
    static void Main(string[] args) {
    /* local variable definition */
    int i, j;
 
    for (i = 2; i < 100; i++) {
      for (j = 2; j <= (i / j); j++)
        if ((i % j) == 0) break; // if factor found, not prime
          if (j > (i / j)) Console.WriteLine("{0} is prime", i);
      }
      Console.ReadLine();
    }
  }
} 

Output:

2 is prime
3 is prime
5 is prime
7 is prime
11 is prime
13 is prime
17 is prime
19 is prime
23 is prime
29 is prime
31 is prime
37 is prime
41 is prime
43 is prime
47 is prime
53 is prime
59 is prime
61 is prime
67 is prime
71 is prime
73 is prime
79 is prime
83 is prime
89 is prime
97 is prime
Infinite Loop

A loop becomes infinite loop if a condition never becomes false. The for loop is traditionally used for this purpose. Since none of the three expressions that form the for loop is required, you can make an endless loop by leaving the conditional expression empty.

Example:

using System;
 
namespace Loops {
  class Program {
    static void Main(string[] args) {
      for (; ; ) {
        Console.WriteLine("Hey! I am Trapped");
      }
    }
  }
} 

When the conditional expression is absent, it is assumed to be true.

C# Program Control Structures

C# [225] Specifying one or more conditions to be evaluated or tested by the program require decision making structures. If the condition is determined to be true or false, the proper statements must be performed.

C# provides following types of decision making statements:




Table 28: C# 2010 Loops Definitions
Sr.No. Loop Type & Description
1 An if statement consists of a boolean expression followed by one or more statements
2 if…else statement – an if statement can be followed by an optional else statement, which executes when the boolean expression is false
3 Nested if statements – you can use one if or else if statement inside another if or else if statement(s)
4 switch statement – a switch statement allows a variable to be tested for equality against a list of values
5 Nested switch statements – you can use one switch statement inside another switch statement(s)
6 The ? Operator
if Statement

An if statement consists of a boolean expression followed by one or more statements. The syntax of an if statement in C# is:

if(boolean_expression) {
 /* Statement(s) will execute if the boolean expression is true */
}

If the boolean expression evaluates to true, then the block of code inside the if statement is executed. If the boolean expression evaluates to false, then the first set of code after the end of the if statement (after the closing curly brace) is executed.

Example:

using System;
 
namespace DecisionMaking {
  class Program {
    static void Main(string[] args) {
      /* Local variable definition */
      int a = 10;
 
      /* Check the boolean condition using if statement */
      if (a < 20) {
        /* If condition is true then print the following */
        Console.WriteLine("a is less than 20");
      }
      Console.WriteLine("value of a is : {0}", a);
      Console.ReadLine();
    }
  }
}

Output:

a is less than 20;
value of a is : 10
if...else Statement

An if statement can be followed by an optional else statement, which executes when the boolean expression is false. The syntax of an if…else statement in C# is:

if(boolean_expression) {
  /* Statement(s) will execute if the boolean expression is true */
} else {
  /* Statement(s) will execute if the boolean expression is false */
}

If the boolean expression evaluates to true, then the if block of code is executed, otherwise else block of code is executed.

Example:

using System;
 
namespace DecisionMaking {
  class Program {
    static void Main(string[] args) {
      /* Local variable definition */
      int a = 100;
 
      /* Check the boolean condition */
      if (a < 20) {
        /* If condition is true then print the following */
        Console.WriteLine("a is less than 20");
      } else {
        /* If condition is false then print the following */
        Console.WriteLine("a is not less than 20");
      }
      Console.WriteLine("value of a is : {0}", a);
      Console.ReadLine();
    }
  }
}

Output:

a is not less than 20;
value of a is : 100
Nested if Statement

It is always legal in C# to nest if…else statements, which means you can use one if or else if statement inside another if or else if statement(s). The syntax for a nested if statement is as follows:

if( boolean_expression 1) {
  /* Executes when the boolean expression 1 is true */
  if(boolean_expression 2) {
    /* Executes when the boolean expression 2 is true */
  }
}

Example:

using System;
 
namespace DecisionMaking {
  class Program {
    static void Main(string[] args) {
      //* Local variable definition */
      int a = 100;
      int b = 200;
 
      /* Check the boolean condition */
      if (a == 100) {
 
        /* If condition is true then check the following */
        if (b == 200) {
          /* If condition is true then print the following */
          Console.WriteLine("Value of a is 100 and b is 200");
        }
      }
      Console.WriteLine("Exact value of a is : {0}", a);
      Console.WriteLine("Exact value of b is : {0}", b);
      Console.ReadLine();
    }
  }
}

Output:

Value of a is 100 and b is 200
Exact value of a is : 100
Exact value of b is : 200
switch Statement

A switch statement allows a variable to be tested for equality against a list of values. Each value is called a case, and the variable being switched on is checked for each switch case.

The syntax for a switch statement in C# is as follows:

switch(expression) {
  case constant-expression  :
    statement(s);
    break; /* Optional */
  case constant-expression  :
    statement(s);
    break; /* Optional */
  
  /* You can have any number of case statements */
  default : /* Optional */
  statement(s);
}

The following rules apply to a switch statement.

  1. The expression used in a switch statement must have an integral or enumerated type or be of a class type in which the class has a single conversion function to an integral or enumerated type.
  2. You can have any number of case statements within a switch. Each case is followed by the value to be compared to and a colon.
  3. The constant-expression for a case must be the same data type as the variable in the switch, and it must be a constant or a literal.
  4. When the variable being switched on is equal to a case, the statements following that case will execute until a break statement is reached.
  5. When a break statement is reached, the switch terminates, and the flow of control jumps to the next line following the switch statement.
  6. Not every case needs to contain a break. If no break appears, the flow of control will fall through to subsequent cases until a break is reached.
  7. A switch statement can have an optional default case, which must appear at the end of the switch. The default case can be used for performing a task when none of the cases is true. No break is needed in the default case.

Example:

using System;
 
namespace DecisionMaking {
  class Program {
    static void Main(string[] args) {
      /* Local variable definition */
      char grade = 'B';
 
      switch (grade) {
        case 'A':
          Console.WriteLine("Excellent!");
          break;
        case 'B':
        case 'C':
          Console.WriteLine("Well done");
          break;
        case 'D':
          Console.WriteLine("You passed");
          break;
        case 'F':
          Console.WriteLine("Better try again");
          break;
        default:
          Console.WriteLine("Invalid grade");
          break;
      }
      Console.WriteLine("Your grade is  {0}", grade);
      Console.ReadLine();
    }
  }
}

Output:

Well done
Your grade is B
Nested switch Statement

It is possible to have a switch as part of the statement sequence of an outer switch. Even if the case constants of the inner and outer switch contain common values, no conflicts will arise.

The syntax for a nested switch statement is as follows:

switch(ch1) {
  case 'A':
    Console.WriteLine("This A is part of outer switch" );
   
  switch(ch2) {
    case 'A':
      Console.WriteLine("This A is part of inner switch" );
      break;
    case 'B': /* Inner B case code */
  }
  break;
  case 'B': /* Outer B case code */
}

Example:
<code C>
using System;

namespace DecisionMaking {
  class Program {
    static void Main(string[] args) {
      int a = 100;
      int b = 200;
         
      switch (a) {
        case 100: 
          Console.WriteLine("This is part of outer switch ");
            
          switch (b) {
            case 200:
              Console.WriteLine("This is part of inner switch ");
              break;
          }
        break;
      }
      Console.WriteLine("Exact value of a is : {0}", a);
      Console.WriteLine("Exact value of b is : {0}", b);
      Console.ReadLine();
    }
  }
} 

Output:

This is part of outer switch
This is part of inner switch
Exact value of a is : 100
Exact value of b is : 200

C# Classes

Defining a Class

A C# [226] class definition starts with the keyword class followed by the class name, and the class body enclosed by a pair of curly braces. Following is the general form of a class definition:

<access specifier> class  class_name {
  //Member variables
  <access specifier> <data type> variable1;
  <access specifier> <data type> variable2;
  ...
  <access specifier> <data type> variableN;
  //Member methods
  <access specifier> <return type> method1(parameter_list) {
    //Method body
  }
  <access specifier> <return type> method2(parameter_list) {
    //Method body
  }
  ...
  <access specifier> <return type> methodN(parameter_list) {
    //Method body
  }
}

Note:

  • access specifiers specify the access rules for the members as well as the class itself. If not mentioned, then the default access specifier for a class type is internal. Default access for the members is private;
  • data type specifies the type of variable, and return type specifies the data type of the data the method returns if any;
  • to access the class members, you use the dot (.) operator;
  • the dot operator links the name of an object with the name of a member.

Example:

using System;
 
namespace BoxApplication {
 
  class Box {
    public double length;   //Length of a box
    public double breadth;  //Breadth of a box
    public double height;   //Height of a box
  }
  class Boxtester {
    static void Main(string[] args) {
      Box Box1 = new Box();   //Declare Box1 of type Box
      Box Box2 = new Box();   //Declare Box2 of type Box
      double volume = 0.0;    //Store the volume of a box here
 
      //Box1 specification
      Box1.height = 5.0;
      Box1.length = 6.0;
      Box1.breadth = 7.0;
 
      //Box2 specification
      Box2.height = 10.0;
      Box2.length = 12.0;
      Box2.breadth = 13.0;
 
      //Volume of Box1
      volume = Box1.height * Box1.length * Box1.breadth;
      Console.WriteLine("Volume of Box1 : {0}",  volume);
 
      //Volume of Box2
      volume = Box2.height * Box2.length * Box2.breadth;
      Console.WriteLine("Volume of Box2 : {0}", volume);
      Console.ReadKey();
    }
  }
}

Output:

Volume of Box1 : 210
Volume of Box2 : 1560
Member Functions and Encapsulation

A member function of a class is a function that has its definition or its prototype within the class definition similar to any other variable. It operates on an object of the class of which it is a member, and has access to all the members of a class for that object.

Member variables are the attributes of an object (from the design perspective), and they are kept private to implement encapsulation. These variables can only be accessed using the public member functions.

Sample:

using System;
 
namespace BoxApplication {
   class Box {
      private double length;   //Length of a box
      private double breadth;  //Breadth of a box
      private double height;   //Height of a box
 
      public void setLength( double len ) {
         length = len;
      }
      public void setBreadth( double bre ) {
         breadth = bre;
      }
      public void setHeight( double hei ) {
         height = hei;
      }
      public double getVolume() {
         return length * breadth * height;
      }
   }
   class Boxtester {
      static void Main(string[] args) {
         Box Box1 = new Box();   //Declare Box1 of type Box
         Box Box2 = new Box();
         double volume;
 
         //Declare Box2 of type Box
         //Box1 specification
         Box1.setLength(6.0);
         Box1.setBreadth(7.0);
         Box1.setHeight(5.0);
 
         //Box2 specification
         Box2.setLength(12.0);
         Box2.setBreadth(13.0);
         Box2.setHeight(10.0);
 
         //Volume of Box1
         volume = Box1.getVolume();
         Console.WriteLine("Volume of Box1 : {0}" ,volume);
 
         //Volume of Box2
         volume = Box2.getVolume();
         Console.WriteLine("Volume of Box2 : {0}", volume);
 
         Console.ReadKey();
      }
   }
}

Output:

Volume of Box1 : 210
Volume of Box2 : 1560
C# Constructors

A class constructor is a special member function of a class that is executed whenever we create new objects of that class.

A constructor has the same name as that of class, and it does not have any return type.

Example:

using System;
 
namespace LineApplication {
   class Line {
      private double length;   //Length of a line
 
      public Line() {
         Console.WriteLine("Object is being created");
      }
      public void setLength( double len ) {
         length = len;
      }
      public double getLength() {
         return length;
      }
 
      static void Main(string[] args) {
         Line line = new Line();    
 
         //Set line length
         line.setLength(6.0);
         Console.WriteLine("Length of line : {0}", line.getLength());
         Console.ReadKey();
      }
   }
}

Output:

Object is being created
Length of line : 6

A default constructor does not have any parameter, but you can make one if you need to pass some setup values on the initialisation – such constructors are called parameterised constructors. This technique helps you to assign an initial value to an object at the time of its creation.

Example:

using System;
 
namespace LineApplication {
   class Line {
      private double length;   //Length of a line
 
      public Line(double len) {  //Parameterized constructor
         Console.WriteLine("Object is being created, length = {0}", len);
         length = len;
      }
      public void setLength( double len ) {
         length = len;
      }
      public double getLength() {
         return length;
      }
      static void Main(string[] args) {
         Line line = new Line(10.0);
         Console.WriteLine("Length of line : {0}", line.getLength()); 
 
         //Set line length
         line.setLength(6.0);
         Console.WriteLine("Length of line : {0}", line.getLength()); 
         Console.ReadKey();
      }
   }
}

Output:

Object is being created, length = 10
Length of line : 10
Length of line : 6
C# Destructors

A destructor is a special member function of a class that is executed whenever an object of its class goes out of scope. A destructor has the same name as that of the class with a prefixed tilde (~), and it can neither return a value nor can it take any parameters. C# (.NET environment) has a built-in memory management system that tracks unused objects and release memory automatically, but in constrained memory systems like RPI, it is sometimes essential to manually notify this mechanism about the possibility to release memory once the object is no longer used. Here destructor helps much. Moreover, destructor brings a possibility to handle hardware-related issues, e.g. close connection, send a farewell message to the external device, etc. Destructors cannot be inherited or overloaded.

Example:

using System;
 
namespace LineApplication {
   class Line {
      private double length;   //Length of a line
 
      public Line() {   //Constructor
         Console.WriteLine("Object is being created");
      }
      ~Line() {   //destructor
         Console.WriteLine("Object is being deleted");
      }
      public void setLength( double len ) {
         length = len;
      }
      public double getLength() {
         return length;
      }
      static void Main(string[] args) {
         Line line = new Line();
 
         //Set line length
         line.setLength(6.0);
         Console.WriteLine("Length of line : {0}", line.getLength());           
      }
   }
}

Output:

Object is being created
Length of line : 6
Object is being deleted
Static Members of a C# Class

We can define class members as static using the static keyword. When we declare a member of a class as static, it means no matter how many objects of the class are created, there is only one copy of the static member.

The keyword static implies that only one instance of the member exists for a class. Static variables are used for defining constants because their values can be retrieved by invoking the class without creating an instance of it. Static variables can be initialised outside the member function or class definition. You can also initialise static variables inside the class definition.

Example:

using System;
 
namespace StaticVarApplication {
   class StaticVar {
      public static int num;
 
      public void count() {
         num++;
      }
      public int getNum() {
         return num;
      }
   }
   class StaticTester {
      static void Main(string[] args) {
         StaticVar s1 = new StaticVar();
         StaticVar s2 = new StaticVar();
 
         s1.count();
         s1.count();
         s1.count();
 
         s2.count();
         s2.count();
         s2.count();
 
         Console.WriteLine("Variable num for s1: {0}", s1.getNum());
         Console.WriteLine("Variable num for s2: {0}", s2.getNum());
         Console.ReadKey();
      }
   }
}

Output:

Variable num for s1: 6
Variable num for s2: 6

You can also declare a member function as static. Such functions can access only static variables. The static functions exist even before the object is created. Example:

using System;
 
namespace StaticVarApplication {
   class StaticVar {
      public static int num;
 
      public void count() {
         num++;
      }
      public static int getNum() {
         return num;
      }
   }
   class StaticTester {
      static void Main(string[] args) {
         StaticVar s = new StaticVar();
 
         s.count();
         s.count();
         s.count();
 
         Console.WriteLine("Variable num: {0}", StaticVar.getNum());
         Console.ReadKey();
      }
   }
}

Output:

Variable num: 3

C# Events

Events occur when a user makes actions like a key press, clicks, mouse movements, etc., or some other occurrence such as system-generated notifications. Applications must respond to events if they occur, i.e. handle interrupts. Events are used during inter-process communication.

Using Delegates With Events

The events are declared and raised in a class. They are associated with the event handlers using delegates within the same class or some other class. To publish the event, the class containing it must be defined. It is called the publisher class. Some other class that accepts this event is called the subscriber class. Events use the publisher-subscriber model.

The object containing a definition of the event and the delegate is named publisher. The event-delegate association is also defined in this object. A publisher class object invokes the event, and it is notified to other objects.

A subscriber is an object that accepts the event and provides an event handler. The delegate in the publisher class invokes the method (event handler) of the subscriber class.

Declaring Events

To declare an event inside a class, first, a delegate type for the event must be declared. For example,

public delegate string MyDel(string str);

Next, the event itself is declared, using the event keyword:

event MyDel MyEvent;

The preceding code defines a delegate named “BoilerLogHandler” and an event named “BoilerEventLog”, which invokes the delegate when it is raised.

Example:

using System;
 
namespace SampleApp {
   public delegate string MyDel(string str);
 
   class EventProgram {
      event MyDel MyEvent;
 
      public EventProgram() {
         this.MyEvent += new MyDel(this.WelcomeUser);
      }
      public string WelcomeUser(string username) {
         return "Welcome " + username;
      }
      static void Main(string[] args) {
         EventProgram obj1 = new EventProgram();
         string result = obj1.MyEvent("Tutorials Point");
         Console.WriteLine(result);
      }
   }
}

Output:

Welcome Tutorials Point

Introduction to the IoT Communication and Networking

Mind, there is “I” in IoT!

In no doubt, IoT is network oriented – even the name IoT naturally relates to the Internet network. Communication is an essential part of IoT idea. Every IoT device must communicate somehow, even most simple, passive RFID tag – it responds with some data to the excitation. Communication is always performed with some rules known for both communicating parties. Like people have their different languages to use, devices have protocols. Communication protocol describes how to address the information to the remote device, how to encode the data, how to check the correctness of the incoming message. The physical layer of protocol description also tells how to transmit every bit of data, what is the frequency of radio waves, how fast we can send the data, what is the maximum range of the transmission.

Communication in IoT devices can be wired or wireless.

IoT networking is much different than typical, multilayered, stack-oriented TCP/IP or similar communication model; we know well while using our PC, MAC, server or Smartphone on a daily basis.

Indeed constrained IoT devices are usually unable to operate regular – full time on, ISO/OSI layered stack, because of constrained resources. In details it primary means, IoT devices are limited by the processor power, RAM and storage sizes and mainly because of limited power resources. IoT device is expected to be energy efficient, thus low powered, that in most cases excludes typical wireless connection standards, e.g. WiFi. On the other hand, IoT devices are expected to communicate over long distances – some couple or a dozen of kilometres – where wired infrastructure like Ethernet cables and related infrastructure is non-existent and most of the wired technologies, copper-based is out of range.

Also, IoT devices daily life-cycle is much different than, e.g. or PC life-cycle. We as humans used to switch on the notebook, work extensively on the web, then put it to the low power or off, making the machine to sleep, hibernate or just shutting it down. And we wake it up when needed. It barely makes network operation during sleep. IoT devices are expected to be sleeping providing low power mode whenever possible, and on the other hand, they're supposed to be fully operable, when only needed. Most performed IoT tasks related to the sensing have cyclical nature, i.e. measuring gases as a sensor-network node whereas period can be something like between seconds and months or even longer. Anyway, they're usually expected to trigger themselves to be awake from sleep, perform some operation and connect to the network.

Because of the existence of different IoT devices including those very constrained from 8-bit processors with some kB of the RAM to 32-bit multicore machines well-replacing PCs, IoT networking is very competitive on protocols, approaches and solutions. There are indeed some networking standards introduced by standardisation organisation like IEEE, yet they are competed by large manufacturers forcing their complex solutions including dedicated hardware, software and protocols. The third force driving this market are open solutions and enthusiasts, usually working with cheap equipment, providing de-facto standards for many hobbyists and also industry.

Following chapters explain some most popular concepts about how to organise network fulfilling the above constraints on communication between IoT devices (Machine-2-Machine) and how to let them communicate with the Internet: including hardware, software and human-users. We focus on the de-facto standards existing in the web, usually as open-source libraries and somewhat low-cost devices.

An interesting survey made by RS components [227] shows 11 wireless protocols used in IoT. Some of them you can use free, without having any license to purchase, while some others are proprietary, some of them need the subscription plan.

Networking Overview

IoT devices are not separated from the global networking environment that nowadays is highly integrated, connecting various wired and wireless transmission standards into one network called the Internet. Indeed some networks are separated because of security and safety reasons and regulations, yet they usually share the same standards that global, Internet network.

XXI century brought wide acceptance of wireless connections. They became very popular even in so-called pico-networks, like your PAN (Personal Area Network)[228], implemented using, e.g. Bluetooth Connection, where your smartphone in the left pocket of your jeans is hosting a server and there are many wireless devices connected to it: your wireless headset/audio device that you're listening to the music ad the moment, wireless HID controller (e.g. MYO device)[229], your Smartwatch, AR glasses, etc. All those devices constitute a personal, wireless network cell, usually also routed via NAT (Network Adress Translation) [230] to the Internet, through some other wireless connection like WiFi or mobile data (3G/4G/LTE). Naturally, many IoT devices share standard wireless protocols, models and ideas, but some of them are not powerful enough to integrate with the Internet. On the other hand, wireless connections are somehow natural to the IoT devices where they are expected to be operating in remote destinations, usually without any wired infrastructure. Because of it, some Internet standards were adapted to their constraints or such networks of constrained devices are separated and gatewayed through some more powerful devices, where protocol translation occurs. Those models are discussed below, in the following chapters.

The similar way to the regular Internet networking, IoT networking is implemented using (usually simplified) layered stack, similar to the regular ISO/OSI 7 layer networking stack well known to all IT students [231], where the lowest 3 levels constitute so-called Media layers of the stack (recommendation X.200).

Figure 366: ISO/OSI multi-layer Internet networking protocol stack.

Level 1 is a Physical Layer (PHY). On top of it, there is level 2 is Data Link Layer with Media Access Control and Logical Link Control (MAC/LLC). Level 3 is the Networking layer (NET) where packets are formed and routed as presented in Figure 366. ISO-OSI model was designed and implemented for dedicated network controller chips and powerful processors; thus not all of the IoT devices are capable to fully implement this model, particularly because of constrained RAM and storage memory sizes. Also, this model requires an instant connection to the remote node (PHY dependent), so it has a strong impact on the battery drain in case of the low power devices.

A quick overview of popular communication technologies for the Internet is presented in Figure 367. Note wide distance range between nodes of the Wireless devices regarding protocol used (mostly because of the PHY nature) where it varies from some meters in case of piconets up to some 180–2000 km when considering LEO (Low Earth Orbit) satellites, e.g. communicating through Iridium network and even up to about 35 786 km in case of the use of the geostationary satellites [232].

Another factor is the communication bandwidth. Fortunately, IoT devices usually do not require high bandwidth – some couple of kbps is enough; thus, almost all protocols apply here.

Figure 367: Popular wireless networking standards.

In many cases, IoT remote, distant nodes do not need constant communication, i.e. weather sensing would better communicate on datagram communication model (UDP rather than TCP) [233]. In such case, IoT devices utilize differently, simplified IoT stack as presented in Figure 368.

Figure 368: Simplified, IoT-oriented implementation of the protocol stack (using UDP).

Communication Models

IoT Devices can be classified regarding their ability to implement full protocol stacks of the typical, Internet protocols like IPv4, IPv6, HTTP, etc.

  • Devices unable to implement full, protocol stack without external support, like, i.e. Arduino Uno (R3) with 32 kb of the flash memory, 16 MHz single core processor and 2 kB of static ram, battery powered, consuming some couple of mW while operating.
  • Devices able to implement full, protocol stack yet still limited by their resources, i.e. ESP8266 and ESP32 chips, battery powered, consuming some dozen or hundred of mW while operating.
  • Devices that do can offer various, advanced network services, capable of implementing protocol stack with ease yet not servers, routers or gateways, i.e. Raspberry Pi and its clones. Usually, DC powered with power consumption far above 1–2 W, usually up to 10–15 W.
  • Dedicated solutions for gateways, routers, usually with embedded, hardware-based implementations of the switching logic, utilising some 10-50W of power.
  • Universal IoT computers (i.e. Intel IoT), using PC-grade processors (x86, but sometime ARM), using some about up to 100 W of power.

Some IoT networks are also constrained by the number of IP addresses available regarding the number of IoT devices ones need to connect, so their topology is a priori prepared as NAT (Network Adress Translation) solution [234] thus it requires automatically use of routers.

IoT devices are usually expected to deliver their data to some cloud for storage and processing while the cloud can send back commands to the actuators/outputs.

Finally, there are security concerns, which make the IoT devices to be put in some separate sub-network and guarded by the firewall.

All of it brings the three, main communication models, explained below.

Device to Device and Industry 4.0 Revolution

Device to device communication model, sometimes referenced as M2M (Machine to Machine communication model) used to be implemented between the homogeneous class of the IoT devices. Nowadays, there is a need to enable heterogeneous systems to collaborate and talk one to another. In a device to a device model, communication is usually held simple, sometimes with niche, proprietary protocols, i.e. ANT/ANT+ [235], sometimes do employs heavy protocols like XML, so there is a need to provide common communication ontologies and semantics. Devices participating in such networks usually act as multimode, constituting self-organising networks, capable of exchanging the data through routing and forwarding as it appears in 6LowPAN networks where nodes may not only act as data producer/consumer but is also expected to act as message forwarder/router.

Device to device model is highly utilized in the Industrial Automation Control systems and recently very popular in developing Industry 4.0 (I4.0) solutions, where manufacturing devices, i.e. robots and other Cyber-Physical systems (CPS) communicate to set operation sequence for optimal manufacturing process (so-called Industry 4.0) thus providing elastic working zones along with manufacturing flexibility and self-adaptation of the processes. It happens because of the presence of various IoT devices (here sometimes referenced as Industrial IoT) and advanced data processing including Big and Small data. Such a device to device networks very frequently mimics popular P2P (peer to peer) networks, where one device can virtually contact any other to ask for information or deliver one. Comparing to the classical, tree-like topology, a device to device communication constitutes a graph of relations rather than a hierarchized tree. The Figure 369 presents comparison between pre-I4.0 (Industry 3.0) and I4.0 data flow. Along with physical (real) devices participating in the manufacturing process, there usually goes their virtual representation (“virtual twin”) to enable cognitive manufacturing based on data science. The detailed description of the data analysis and its use in I4.0 is out of the scope of this book, however.

Figure 369: Industry 3.0 vs Industry 4.0 communication topology.

The device to device communication assumes, participating devices are smart enough to talk one another, without the need of the translation nor advanced data processing, even if their nature is different (e.g. your intelligent door can inform your smart, IoT kettle to start boiling water once they get informed about poor weather condition by the Internet weather monitoring service, when you're back home after long day of work). Devices constituting mesh or scatter network communicate virtually one another similar way people do. The Figure 370 briefly presents the data flow idea

Figure 370: Device to device communication model.

Device to Gateway

Device to gateway communication occurs when there is a need to provide the translate information between different networks, i.e. some Zigbee [236] network devices need to send data to the Internet to let the smart home be remotely monitored and managed. This model also appears when there is a need to transfer messages between IoT network implemented with constrained devices, so using some simplified protocol (e.g. LoRA, 6LowPAN) and the Internet network, using the full implementation of the protocols (e.g. IP6). In this case, the gateway device (sometimes named here as Edge Router) needs to have knowledge about constrained devices constituting IoT network and it usually supplies some missing information instead of them, i.e. enriching message headers or addresses when passing packets from IoT constrained network to the Internet, but also translating Internet packets (e.g. by removing full address), when acting opposite, e.g. forwarding actuator requests to the IoT devices.

Gatewaying and protocol translation can also occur on the 6th and 7th level of the ISO/OSI model when the implementation of high-level protocols overwhelms even more advanced IoT devices, i.e. simple MQTT texting can be converted to the XML, heavy messages or exposed as XHTML. Those solutions are mostly software-based, i.e. Node-RED [237]. The Figure 371 briefly presents the data flow. Please note the protocol change: arrows of the different colours reflect it.

Figure 371: Device to gateway communication model

Device to Cloud

As IoT devices are usually unable to constitute an efficient computation structure (as single IoT node or even their federation), most data is forwarded to the server, often a cloud-based solution, where it is stored and processed. This data processing in the cloud varies, depending on the type of information, their goal, etc. In any case, we usually face the problem of the visualisation, data analytics (statistics, data mining, knowledge discovery, big data processing). Those tasks are resource consuming, require huge processing capabilities; thus, utilising cloud solution is usually a good choice. Note, claiming “cloud” we consider not only public clouds like Amazon, Google or Microsoft but also dedicated solutions hidden somewhere in the separated, manufacturing networks. Eventually, there is a need to send back some actuation requests to the devices, from the cloud. Cloud services are usually PC based solutions, thus they extensively use rich protocols, providing their APIs via i.e. REST [238], SoAP [239], HTTP GET/POST methods [240], etc. It requires the IoT devices interfacing cloud to implement full communication stacks for the protocols needed. Some of the IoT devices can interface cloud services directly, but some of them are unable to do so due to the constraints, so it is necessary to use gateways as mentioned in the previous chapter.

Figure 372: Device to cloud communication model.

Media Layers – Wired Networking

While the IoT ecosystem is usually considered to be composed of wireless devices, it is still possible to connect IoT solutions using a wired connection. In this chapter, we do not present communication protocols that are short distant one designed to connect sensors to IoT device, like I2C, SPI, Serial, etc. Those are described in chapter “4.2. Embedded Systems Communication Protocols”.

When wireless-enabled SoCs where about to be delivered to the market (i.e. ESP8266), there were already available sorts of extension devices for popular Embedded systems, like i.e. Ethernet Shield for Arduino boards.

Figure 373: Ethernet shields for Arduino boards.

Cooper based wired networks also bring an extra feature to the IoT designers – an ability to power the device via a wired connection, i.e. PoE (Power over Ethernet) – 802.3af, 802.3at, 802.3bt [241]. Long distance connections may be implemented using optic-based, fibre connections, but those require physical medium converters that are usually quite complex, pretty expensive and power consuming; thus, they apply only to the niche IoT solutions. Please note, mentioned optical connections do not cover so-called LiFi, as those are considered to be wireless [242].

A non exhaustive list of some present and former wired networking solutions are presented in the Table 29.

Table 29: A Short Review of the Most Popular Wired Networking Standards
Name Communication medium Max speed Topology Max range (single segment, passive)
Ethernet Twisted pair: 10BaseT
Coaxial: 10Base2/10Base5
Fibre: 10BaseF
10 Mbps Bus, Star, Mixed (Tree) 10Base2: 0.5–200 m (185 m)
10Base5: 500 m
10BaseT: 100 m (150 m)
10BaseF: 2 km (multimode fibre)
Fast Ethernet Twisted pair: 100BaseTx
Fibre: 100BaseFx
100 Mbps Star 100BaseTx: 100 m (Cat 5)
100BaseFx: 2 km
Gigabit Ethernet Twisted pair: 1000BaseT
Fibre: 1000BaseX (LX/CX/SX)
1000BaseT: 1 Gbps
1000BaseX: 4.268 Gbps
Star 1000BaseT: 100 m (Cat 5)
1000BaseLX: 5 km
Local Talk (Apple) Twisted pair 0.23 Mbps Bus, Star (PhoneNet) 1000 ft
Token ring Twisted pair 16 Mbps Star wired ring 22.5 m / 100 m (cable dependent)
FDDI Fibre 100 Mbps (200 Mbps on two rings, but no redundancy) Dual ring 2 km

Nowadays, the most popular wired networks are 10/100/100BaseT – twisted pair with Cat 5, 5e and 6 cables. They require the IoT system to implement full TCP/IP stack to operate seamlessly with conventional Internet/Intranet/Extranet networks. Because it is usually out of the scope of standard Arduino Uno processor capabilities to implement full TCP stack, there are typically dedicated processors on the network interfaces that assist the central processor or even handle all networking tasks themselves.

Media Layers – Wireless Protocols

Wireless connections define core communication for IoT devices. Wast and growing amount of protocols, their variations and dynamic IoT networking market, all present non-solid situation where old “adult” Internet protocols coexist along with new ideas and IoT hardware and software platforms are more and more capable with every new generation; thus new ideas appear almost daily. Currently, there are many IoT networking protocols defined for various layers of the protocol implementation stack, some of them compatible while others are concurring. The Figure 374 presents some selected protocols existing for IoT. Please note, this covers only the most popular ones and presents a non-exhaustive view. We discuss them more detailed below.

Figure 374: IoT protocols.

PHY + MAC + LLC Layers

Below we present currently most popular, wireless protocols review for the lower ISO/OSI layers (1–2, some of them also implement layer 3 – Networking).

WiFi

WiFI is the set of standards for wireless communication using the 2.4 GHz or 5 GHz band, slightly different spectrum in different countries. The core specification of the 2.4 GHz contains 14 channels with 20 MHz (currently 40 MHz) bandwidth. While there is no centralised physical layer controller, collisions frequently occur even more with a growing number of devices sharing the band. The collision is handled using CSMA-CA with a random binary exponential increase of repeating time.

With the high speed of transmission and range usually not exceeding 100 m, it is widely used as the direct replacement of wired Ethernet in local area networks. It is a very good choice while the amount of data to be transferred is larger, for example, video streams or assembled IoT stream delivered by gateways. It is also possible to use it in direct connectivity for smart sensors, and other IoT elements, but the protocol itself is not designed to transmit small data packets. For many IoT applications, it is too much energy consuming, especially when it comes to battery-powered devices. Moreover, WiFi itself offers only 1-to-1 or star-like models of connection, where the central point is a WiFi Access Point (1-to-many) and does not provide mechanisms for io.e. self-reorganizable, failure-tolerant mesh networks. WiFi becomes a more and more popular choice for not-so-constrained IoT devices because they need to implement full TCP/IP stack and those devices that are also not so constrained by power consumption.

Table 30: WiFi Standards Summary
802.11 standard Transmission speed Frequency
802.11b 11 Mbps 2.4 GHz
802.11g 54 Mbps 2.4 GHz
802.11n 150 Mbps 2.4 GHz
300 Mbps 5 GHz
802.11ac 1 Gbps 5 GHz

Bluetooth

Bluetooth is a prevalent method of connecting a variety of devices in short distance. Almost every computer and a smartphone have Bluetooth module built-in. Standard has been defined by Bluetooth SIG (Special Interest Group) founded in 1998. Bluetooth operates in the 2.4 GHz band with 79 channels with automatic channel switching when interference occurs (hopping frequency). The single channel offers up to about 1Mbps (where around 700kbps is available for the user) bandwidth, and it provides communication within the range from up to 1 m (class 3, 1 mW) till up to 100 m (class 1, 100 mW). The most popular version is class 2 with 10 m range (2.5 mW).

Every Bluetooth device has a unique, 48-bit MAC address.

Bluetooth offers various “profiles”, for multimedia, serial ports, packet transmission encapsulation (PAN), etc. The most useful for IoT devices is PAN (Personal Area Network) Profile and of course SPP (Serial Port) Profile.

Now Bluetooth covers two branches: BR/EDR (Basic Rate/Enhanced Data Rate) for high-speed audio and file transfer connections and LE (Low Energy) for short burst connections [243].

Classical (prior to BLE and 4.0) Bluetooth networks can create ad-hoc, so-called WPAN (Wireless Personal Area Networks) sometimes referenced as Piconets. Bluetooth Piconet can handle up to 7 + 1 devices, where 1 device acts as Master, and it can contact up to 7 Slave devices. Only the Master device can initiate a connection. Fortunately for the IoT approach, much Bluetooth hardware can act as Slave and Master simultaneously, constituting this way a kind of router; thus, devices can constitute a tree-like structure called Scatternet.

Figure 375: Bluetooth Scatternet.

Bluetooth Low Energy (BLE) uses an simplified implementation of the state machine thus is more constrained-devices friendly. It offers a limited range, and it is designed to expose the state rather than transmit streamed data. It provides a speed reaching up to about 1.4 Mbps (2 Mbps aerial throughput) if needed, however. It uses 2.4 GHz band but is designed to avoid interference with WiFi AP and clients. Communication is organised into three advertising channels (located “between” WiFi) and 37 communication channels.

Latest Bluetooth implementations (protocol version 5.0 and newer, implemented in mid-2017) offer a Bluetooth mesh network extending ubiquitous connectivity via many-to-many communication model, dedicated to IoT devices, lighting, Industry 4.0, etc. The Bluetooth mesh is layer-organised, and since there is no longer Master-Slave model used, but messages are relayed through the mesh, it is considered to be no longer the Scatternet because of its flat structure [244]. Sample Bluetooth Mesh Network is presented in Figure 376 (source: Bluetooth SIG, Mesh Profile Specification v1.0 [245]).

Figure 376: Example Topology of the Bluetooth 5 Mesh Network.







Table 31: Bluetooth Standard Summary
Bluetooth Transmission speed Remarks
1.0 21 kbps Few implementations
1.1 124 kbps
1.2 328 kbps First popular version
2.0 + EDR 3 Mbps Extended Data Rate
3.0 + HS 24 Mbps High Speed
3.1 + HS 40 Mbps
4.0 + LE 1 Mbps Low Energy
4.1 Designed for IoT
5.0 One standard for all purposes

Cellular

Cellular (mobile/GSM) networks are one of the possible options because of its wide coverage and long range. Those network use orthogonality in frequency and time spaces. Cellular networks are presented by the subsequent generations (G) – currently up to 4.5G present on the market and 5G in the experimental phase (should be fully functional around the year 2020). Typical GSM network technology, sometimes referenced as an era, runs out within about 10–15 years. It is pretty close but still less than expected end-of-life for classes of IoT devices (15-25 years). GSM hardware used to be backwards compatible, enabling users to access older, even before 2G GSM networks with latest chips.

Figure 377 present GSM network evolution over time and generations. Cellular networks use different frequencies in different countries, yet available radio implementations nowadays are usually able to handle all of them.

Figure 377: GSM network evolution and generations.

Figure 378 presents sample GSM hardware (separate module and ready shield for the Arduino platform).

Figure 378: Sample GSM hardware for IoT prototyping.

GSM protocols are proprietary, quite complex (including advanced ciphering) and require dedicated hardware. A sort of documentation and standards is not publicly available because of security considerations (i.e. voice transmission ciphering details).

On the one hand, the GSM network seems to be a good solution for extended distant IoT networks; on the other, they have many disadvantages, however. First of all, they require the use of operators' infrastructure – as GSM bands are not free to use.

Important! Professional operation requires licencing and connecting existing infrastructure involves a purchase of the unique identifier (phone ID and a number that is given by the SIM card, physical or virtual) and a service fee.

By the limited access constraints there do exist one more – GSM boards are using quite a significant amount of energy when establishing a connection because they need to broadcast their existence as far as possible, to gain a connection with a possibly distant-located base station. It requires tremendous power and drains the battery (even up to 10 W peak); thus, cellular solutions are not suitable for the IoT devices that use frequent data communication.

ZigBee

ZigBee protocol is so far very popular in Smart House but also in Industry appliances. Zigbee is a wireless technology developed as an open standard to address the needs of low-cost, low-power wireless machine to machine networks. It is more popular in the industry, however, but because of the relatively higher cost of equipment in comparison with WiFi, Bluetooth or other RF modules. The Zigbee standard operates on the radio bands 2.4 GHz for smart home applications, 915 MHz in US and Australia, 868 MHz in Europe and 784 MHz in China. The advantage of ZigBee is the possibility of forming the mesh networks where nodes are interconnected with others, so there are multiple paths connecting each pair of nodes. Connections are dynamically updated, so when one node turns off the path going through that node will be automatically rerouted via another path. Transmission speed is up to 250 kbps, theoretical range up to 100 m but usually to some 10–30 m. ZigBee does not provide direct, unique IP-addressing on the Networking layer like 6LowPAN or Thread do. Single ZigBee network can handle up to 65 000 devices.

Z-Wave

Z-Wave is a protocol similar in principals to the ZigBee, but hardware is cheaper; thus, it is more towards inexpensive home automation systems. Like in ZigBee, Z-Wave operates on different frequencies depending on the world region, usually between 865 MHz and 926 MHz. Transmission speed is up to 200 kbps, and the range is up to 100m. A single Z-Wave network is pretty limited on a number of concurrent devices in one network, that is only 232 devices. Each Z-Wave network has a unique ID, and each node (device) in a network has a unique 8-bit identifier.

Thread

Another standard [246] that works using the same 802.15.4 radio. There are some differences in the protocol, like address allocation. In 6LowPAN it is done be nodes since in Thread addresses are obtained from DHCPv6 server.

NFC

NFC (Near Field Communication) is a technology that enables two-way interactions between electronic devices. What is important one of the devices does not have to be equipped with the power source – it is powered by the receiving radio signal. That’s why NFC is used in contactless card technology enabling devices to exchange the data at a distance of less than 4 cm. Transmission speed varies between 100–420 kbps, range between both active devices is up to 10 cm, operating frequency 13.56 MHz.

Sigfox

Sigfox [247] is the idea to connect objects with sub 1 GHz radio frequency. It uses the 900 MHz frequency range from the ISM band. The range is about 30–50 km (open space), 3–10 km (urban environments). This standard uses a technology called Ultra Narrow Band (UNB). It has been designed to transmit data with deficient speed – from 10 to 1000 bps. Thanks to small data packets it consumes only 50 mW of power. It is intended to create the public networks only so using Sigfox requires the subscription plan. Many (but not all) European countries are covered with Sigfox.

LoRaWAN

LoRa (Long Range) is the technology for data transmission with relatively low speed (20 bps do 41 kbps) and the range about 2 km (new transceivers can transmit data up to 15 km). It uses CSS (Chirp Spread Spectrum) modulation in the 433 MHz ISM radio band. The cell topology is the star with the gateway placed at the central point. End-devices use one hop communication with the gateway, that is connected to the standard IP network with a central network server. The LoRa technology is supported as LoRa WAN by LoRa Alliance [248] designed as Sigfox for public networks, but it can also be used in private networks that do not require a subscription.

NET (NWY) Layer

Traditionally, we use IP addressing (usually masked by DNS to be more user-friendly) when accessing Internet resources. IoT devices may also benefit from this approach. However, constrained devices do require special “editions” of the conventional protocols, that are lightweight. Networking layer implements the basic communication mechanisms on the packet level like routing, delivery, proxying, etc. Many IoT, lightweight implementations of the protocols presented below benefit or at least inherit ideas from regular “adult” implementations. Please note that some protocols implement more than one layer, as presented on image 374. We also provide a short reference of the IPv4 and IPv6, to show advantages and drawbacks.

IPv4

Internet Protocol v4 (1981) is perhaps the most widespread networking protocol. The predecessor of the IPv4 protocol originally called IP was introduced in 1974 and supported up to 2^8 hosts, organised in 2^4 subnetworks (RFC 675).

In IPv4 (RFC 760/RFC791) the logical addressing space was extended to 2^32 devices that seemed to be quote much in 1981, but now we struggle with lack of free addressing space. This number is less because some addresses are reserved, e.g. for broadcasting and due to the existence of different classes of addresses and their pools [249]. Sample IPv4 address is, for example, 192.168.1.1.

Some relief to suffocating Internet was brought as an ad-hoc solution with an introduction of the NAT (Network Address Translation). NAT-enabled subnetworks are those, where one public address represents a set of devices hidden behind the router, but that limits usability because of lack of direct access and unique identification in the global network of the devices sharing private address spaces. Even so, there are about 8.5 billion IoT devices expected to be connected to the Internet by the end of the 2017 year, according to the Gartner's report [250]. They all need to be uniquely addressed!

IPv6

IPv6 is the next generation of the IPv4 protocol. It is supposed to replace IPv4, but this process is somehow not so quick as there are many solutions still present on the Internet and Intranets that implement IPv4 only and would become inoperable if IPv4 would not be available anymore. IPv6 brings addressing space large enough to cover all existing and future needs. The number of possible addresses is 2^128. Addresses are presented by 8 groups of 4 hexadecimal values, e.g. 2001:0db8:0000:0042:0000:8a2e:0370:7334.

This brings the capability to uniquely identify any device connected to the Internet using its IPv6 address. Regarding IoT, implementations have many drawbacks (IPv4 also has them). IPv6 network is star-like, whereas IoT networks can benefit from the mesh model. IPv6 network requires a controller providing free addresses (a DHCP server) – devices need to contact it to obtain the address. Every single IoT device needs to keep a list of devices it corresponds with (ARP) to resolve their physical address. Moreover, full IPv6 stack implementation requires large RAM, when used.

6LoWPAN

The name is the abbreviation of “IPv6 over Low-Power Wireless Personal Area Networks” [251] and as it says is the IP based network. This protocol was introduced as a lightweight version of full IPv6, IoT-oriented. This feature allows connecting 6LoWPAN networks with other networks using so-called Edge Router. Thus every node can be visible on the Internet as states in IoT idea. This standard has been developed to operate on the radio channel defined in 802.15.4 (as ZigBee, Z-Wave). It creates the adaptation layer that allows using IPv6 over 802.15.4 link. 6LoWPAN has been adopted in Bluetooth Smart 4.2 standard as well.

6LoWPAN supports two addressing models: 64 bit and 16 bit (that, of course, limits the number of devices connected to one network to 64 000 nodes). The primary frame size is just 127 bytes (comparing to full IPv6 where it is 1280 bytes at least). 6LoWPAN supports unicast and broadcast. It also supports IP routing and link-layer mesh (802.15.5) that enables the introduction of the fail-safe redundant, self-organising networks, because the link-layer mesh can have more than one Edge Router. 6LoWPAN uses autoconfiguration for neighbour devices discovery so does not require a DHCP server. It also supports ciphered transportation using AES 128 (and AES 64 for constrained devices).

Figure 379: Sample 6LoWPAN and Internet

6LoWPAN devices can be just nodes (Hosts) or nodes with routing capability (Routers) as presented in Figure 379.

A gateway between 6LoWPAN and regular IPv6 (IPv4) network is implemented by the Edge Router. Its purpose is to translate “compressed” IPv6 addresses to ensure bi-directional communication between the Internet and 6LoWPAN nodes. Note – the network structure of the 6LoWPAN is logically flat (star/mesh with single addressing space), and devices have unique MAC addresses to be recognisable by the Edge Router device.

When the 6LoWPAN network starts, there are three operations done, repeated consequently.

  1. Commissioning – establishes connectivity on the Data Link Layer level between nodes.
  2. Bootstrapping – performs address configuration, discovery and registration.
  3. Route Initiation – executing routing algorithms to set up paths.

Typical IPv6 networking discovery won't work here because multicast/broadcast messages are not passable through 6LoWPAN routing nodes (routes as on in Figure 380.

An interesting procedure is performed when an IoT node (device) wants to connect to the existing 6LoWPAN network. As there is no central DHCP server broadcasting information, the device needs to discover the configuration and create 6LoWPAN address itself. It issues the network discovery process.

Network discovery (discovery of neighbour nodes) in 6LoWPAN uses four principals:

  • NR – node registration,
  • NC – node confirmation,
  • DAD – duplicate address detection,
  • support for Edge Routers.
Figure 380: 6LoWPAN Network Automated Discovery demystified.

Network Automated Discovery is composed of two main sections.

  1. Part one (dark blue) – Neighbor Discovery (ND):
    • new node sends RS multicast (SLLAO);
    • all routers respond unicast RA (PIO + 6CO + ABRO + SLLAO);
    • node selects one router as default (usually first RA obtained) and derives global Ipv6 address based on prefix delivered (PIO);
    • node sends ARO (ARO + SLLAO) unicast to the selected router;
    • router returns ARO with a status:
      • status is: OK, Duplicate Address, Cache Full, other (see RFC5226);
      • assuming status OK, the router adds new neighbour node address into the cache;
    • Node sends periodically NS to inform that “it is alive” the router (so-called NUD (Neighbor Unreachability Detection));
  • Process above may involve DAD (Duplicate Address Detection) mechanism to be triggered.
    • on registration, router “asks” all Edge Routers if address offered by the node is unique (DAR/DAC messages).
    • DAD message is expected to “wake up” IoT device from standby mode!
  1. Part two (red) – Network Registration (NR):
    • node sends DODAG Solicitation (DIS) unicast to the router.
    • router responds with DODAG Information Object (DIO) and keeps broadcasting it periodically. DIO contains router rank (i.e. it presents, how far the router is from the Edge Router);
    • if node obtains DIO with better rank, it should re-register with other “better” router as a new default router;
    • finally node sends Destination Advertising Object (DAO) to its default router that is forwarded to edge router;
    • edge router responds with DAO ACK.

This way, the new 6LoWPAN node can join the new network seamlessly. Moreover, this mechanism enables 6LoWPAN mesh network to self-organise itself if needed, e.g. in case of a failure of the router.

Host Layer Protocols

The host layers protocols include session (SES), presentation (PRES) and application (APP) level, particularly APP (application) layer in the regular Internet communication is dominated by the HTTP protocol and XML-related derivatives, e.g. SoAP. Also, FTP protocol for file transfer is ubiquitous; it exists since the beginnings of the Internet. Most of them are somehow related to the text. They're referenced as “WEB” protocols. Although these protocols are frequently used by advanced and more powerful IoT devices, this is problematic to be implemented in the constrained IoT devices world. Event simplest HTTP header occupies at least 24 + 8 + 8 + 31 bytes without any payload! There is also a problem to cross firewall boundaries when communication between subnetworks of the IoT devices is expected to occur. Some IoT designed protocols are reviewed below.

MQTT

MQTT protocol [252] was invented especially for the constrained IoT devices and low bandwidth networks. It is located in APP layer 7 of the ISO/OSI stack, but in fact, it covers all layers 5–7. It is text-based protocol yet very compact end efficient. Protocol stack implementation requires about 10 kB of RAM/flash only.

MQTT uses TCP connection so requires open connection channel (this is in opposite to UDP connections, where communications work in a way: “send and forget”). It is considered a drawback of the original MQTT protocol, but there do exist MQTT variations for non-TCP networks, e.g. MQTT-SN. Protocol definition provides reliability and delivery ensure mechanisms.

The standard MQTT Message header is composed of just two bytes only (Table 32)! There are 16 MQTT messages types. Some messages types require variable length header.

Table 32: MQTT Standard Message Header
bit 7 6 5 4 3 2 1 0
byte 1 Message Type DUP flag Qos level RETAIN
byte 2 Remaining length

MQTT requires for its operation a centralized MQTT broker that is located outside of firewalls and NATs, where all clients connect, send and receive messages via publish/subscribe model. The client can act as publisher and subscriber simultaneously. The image 381 presents publish-subscribe model idea.

Figure 381: MQTT broker, publishers and subscribers.

MQTT Message

MQTT is a text-based protocol and is data-agnostic. A message is composed of a Topic (text) and a Payload (data). The topic is a directory-like string with slash (”/“) delimiter. Thus all Topics constitute (or actually may represent) a kind of tree-like folders, similar to those on the file system. The subscriber can subscribe to specific, single Topic, or to a variety of Topics using wildcards, where:

  • # stands for the entire branch,
  • + stands for the single level.

Example Scenario

Publishers deliver some air quality information data in separate MQTT messages and for various rooms at the department Inf of the Universities (SUT, RTU, ITMO) to the Broker:

Topic (publishers):
SUT/Inf/Room1/Sensor/Temperature
SUT/Inf/Corridor/Sensor/Temperature
SUT/Inf/Auditorium1/Sensor/Temperature
RTU/Inf/Room1/Sensor/Temperature
ITMO/Inf/Room1/Sensor/Temperature
RTU/Inf/Room1/Sensor/Humidity
SUT/Inf/Room3/Sensor/Temperature
RTU/Inf/Room1/Window/NorthSide/State

The subscriber 1 wills to get all sensor data for SUT university, Inf (informatics) department only, for any space:

Topic (subscription):
SUT/Inf/+/Sensor/#

The subscriber 2 wills to get only Temperature data virtually from any sensor and in any location in ITMO:

Topic (subscription):
ITMO/#/Temperature

The subscriber 3 wills to get any information from the sensors, but only for the RTU

Topic (subscription):
RTU/#

The payload (data) of the message is text as well, so in case one need to send binary data, it is necessary to encode it (e.g. Base64).

MQTT Broker

MQTT Broker is a server for both publishers and subscribers. The connection is initiated from the client to the Broker, so assuming it is located outside of a firewall, it breaks firewall its boundaries. The Broker provides QoS (Quality of Service), and it can retain message payload. There are three levels of MQTT Broker QoS (supplied in the message level).

  • Unacknowledged service: Ensures that MQTT message is delivered at most once to each subscriber.
  • Acknowledged service: Ensures delivery of the message at least once to every subscriber. The broker expects acknowledgement to be sent from the subscriber. Otherwise, it retransmits data.
  • Assured service: This is two-step delivery of the message, and ensures the message is delivered exactly once to every subscriber.

For Acknowledged and Assured services it is vital to provide unique packet IDs in MQTT frame.

The DUP flag (byte 1, bit 3) represents information sent by the publisher if the message is a “first try” (0) or a retransmitted one (1). It is mostly for internal purposes, and this flag is never propagated to the subscribers.

MQTT offers some limited set of features (options):

  • clean session flag for durable connections:
    • if set TRUE, Broker removes all of the client subscriptions on client disconnect;
    • otherwise Broker collects messages (QoS depending) and delivers them on client reconnecting; thus, connections remain idle;
  • MQTT “will”: on connection lost, Broker will automatically “simulate” publishing of the pre-defined MQTT message (topic and payload). All clients subscribing this message (whether directly or via a wildcard) will be notified immediately. It is a great feature for failure/disaster discovery;
  • message retaining: it is a feature for regular messages. Any message can be set as retaining and in such case Broker will keep the last one. Once a new client subscribes topic, it will receive a retained message immediately even if the publisher is not publishing any message at the moment. This feature is last known good value. It is good to present publishers state (e.g. publisher sends retained message meaning “I'm going offline” and then disconnects. Any client connecting will be notified immediately about the device (client) state.

Interestingly MQTT is a protocol used by Facebook Messenger [253]. It is also implemented natively in Microsoft Azure and Amazon Web Services (among many others).

MQTT security is rather weak. MQTT Broker can offer user and password verification yet it is sent plain text. However, all communication between client and Broker may be encapsulated in SSL, encrypted stream.

A short comparison of MQTT and HTTP protocols are presented in the Table 33.

Table 33: MQTT vs HTTP
MQTT HTTP
Design Data centric Document centric
Pattern Publish/Subscribe Request/response
Complexity Simple Complex
Message size Small, with 2 byte binary header Larger with text based status
Service levels 3 QoS None
Implementation C/C++: 10–30 kB
Java ~100 kB
Depends on application but hits > MB
Data distribution models 1-to-1
1-to-N
1-to-1

CoAP

CoAP protocol (RFC7252) originates from the REST (Representational State Transfer). CoAP does not use a centralised server as MQTT does, but every single device “hosts” a server on its own to provide available resources to the clients asking for service offering distributed resources. CoAP uses UDP (comparing to MQTT that uses TCP) and is stateless thus does not require memory for tracking the state. The CoAP implementation assumes every single IoT device has a unique ID, and things can have multiple various representations. It is intended to link “things” together using existing standard methods. It is rather a resource-oriented (not document-oriented like HTTP/HTML), designed for slow IoT networks with a high degree of packet loss, also support devices to be periodically offline. CoAP uses URIs :

  • "coap:" "//" host[":"port] path ["?" query] to access a service/resource,
  • a secure, encrypted version uses “coaps” instead of “coap”.

It supports various content types, can work with proxy and can be cached.
The protocol is designed to be compact and simple to implement. The stack implementation takes only about 10 kB of RAM and 100 kB of storage. The header is only 4 bytes.

CoAP protocol has a binary header to decrease overhead but payload depends on the content type. Initial, non-exclusive list of the payload types include:

  • text/plain (charset=utf-8) (ID=0, RFC2046, RFC3676, RFC5147),
  • application/link-format (ID=40, RFC6690),
  • application/xml (ID=41 RFC3023),
  • application/octet-stream (ID=42, RFC2045, RFC2046),
  • application/json (ID=50, RFC7159).

CoAP endpoint services are identified by unique IP and port number. However, they operate on the UDP instead of TCP (like, e.g. HTML does). The transfer in CoAP is made using non-reliable UDP network so that a message can appear duplicated, disappear or it can be delivered in other order than initially sent. Because of the nature of datagram communication, messages are exchanged asynchronously between two endpoints, transporting Requests and Responses. CoAP messages can be (non-exhaustive list):

  • CON (Confirmable, those requiring ACK Acknowledge),
  • NON (Non-Confirmable, those that do not need ACK),
  • ACK (an acknowledgement message),
  • RESET (sent if CON or NON was received, but the receiver cannot understand the context, e.g. part of the communication is missing because of device restart, messages memory loss, etc.). Empty RESET messages can be used to “ping” the device.

Because of the UDP network characteristics, CoAP provides an efficient yet straightforward reliability mechanism to ensure successful delivery of messages:

  • stop and wait for retransmission with exponential back-off for CON messages,
  • duplicate message detection for CON and NON-messages.

Request-reponse pair is identified by a unique “Token”. Sample request-response scenarios are presented in images below. Sample CoAP message exchange scenarios between client and server are presented (two per image) in Figure 382 and Figure 383.

Figure 382: CoAP scenario 1: confirmable with time delay payload answer (0 × 70) and immediate payload answer (0 × 71).
Figure 383: CoAP scenario 2: urecognized request (0 × 72) and non-confirmable request (0 × 73).

The scenario in the Figure 382 (left, with token 0 × 70) is executed in situation when a CoAP server device (a node) need some time to prepare data and cannot deliver information right away. The scenario in Figure 382 (right, with token 0 × 71) is used, when a CoAP server can provide information to the client immediately. The scenario in Figure 383 (left, with token 0 × 72) appears when a CoAP server cannot understand the request. The scenario in Figure 383 (right, with token 0 × 73) presents the situation where the request to the CoAP server was made with a non-confirmable request.


[1] “ITU Internet Reports 2005: The Internet of Things.” http://www.itu.int/osg/spu/publications/internetofthings/
[2] “Special Report: The Internet of Things”, in “the Institute”, IEEE 2014, http://theinstitute.ieee.org/static/special-report-the-internet-of-things
[3] “Towards a definition of the Internet of Things (IoT)”, IEEE 2015
[4] Standard for an Architectural Framework for the Internet of Things (IoT) http://grouper.ieee.org/groups/2413/
[5] Ovidiu Vermesan, Peter Friess (eds.): Digitising the Industry, Internet of Things Connecting the Physical, Digital and Virtual Worlds, River Publishers Series in Communications, 2016
[6] Vision and Challenges for Realising the Internet of Things, CERP-IoT 2010, http://www.internet-of-things-research.eu/pdf/IoT_Clusterbook_March_2010.pdf
[7] Salim Elbouanani, My Ahmed El Kiram, Omar Achbarou: “Introduction To The Internet Of Things Security. Standardisation and research challenges”, 2015 11th International Conference on Information Assurance and Security (IAS), IEEE 2015
[8] Ovidiu Vermesan, Peter Friess (eds.): Digitising the Industry, Internet of Things Connecting the Physical, Digital and Virtual Worlds, River Publishers Series in Communications, 2016
[9] Ala Al-Fuqaha, Mohsen Guizani, Mehdi Mohammadi, Mohammed Aledhari, Moussa Ayyash: Internet of Things: A Survey on Enabling Technologies, Protocols and Applications, IEEE Communications Surveys & Tutorials, Volume: 17, Issue: 4, 2015
[10] Arslan Munir, IFCIoT: Integrated Fog Cloud IoT Architectural Paradigm for the Future Internet of Things, IEEE Consumer Electronics Magazine, Vol. 6, Issue 3, July 2017
[11] Arslan Munir, IFCIoT: Integrated Fog Cloud IoT Architectural Paradigm for the Future Internet of Things, IEEE Consumer Electronics Magazine, Vol. 6, Issue 3, July 2017
[46] Internet of Things: Architectures, Protocols, and Applications; P. S. Smruti, R. Sarangi. https://doi.org/10.1155/2017/9324035
[47] Internet of Things: Security Vulnerabilities and Challenges; I. Andrea, C. Chrysostomou, G. Hadjichristofi, The 3rd IEEE ISCC 2015 International Workshop on Smart City and Ubiquitous Computing Applications, https://doi.org/10.1109/ISCC.2015.7405513
[48] Rajan Arora, I2C Bus Pullup Resistor Calculation, Texas Instruments Application Report
[67] “ITU Internet Reports 2005: The Internet of Things.” http://www.itu.int/osg/spu/publications/internetofthings/
[68] “Special Report: The Internet of Things”, in “the Institute”, IEEE 2014, http://theinstitute.ieee.org/static/special-report-the-internet-of-things
[69] “Towards a definition of the Internet of Things (IoT)”, IEEE 2015
[70] Standard for an Architectural Framework for the Internet of Things (IoT) http://grouper.ieee.org/groups/2413/
[71] Ovidiu Vermesan, Peter Friess (eds.): Digitising the Industry, Internet of Things Connecting the Physical, Digital and Virtual Worlds, River Publishers Series in Communications, 2016
[72] Vision and Challenges for Realising the Internet of Things, CERP-IoT 2010, http://www.internet-of-things-research.eu/pdf/IoT_Clusterbook_March_2010.pdf
[73] Salim Elbouanani, My Ahmed El Kiram, Omar Achbarou: “Introduction To The Internet Of Things Security. Standardisation and research challenges”, 2015 11th International Conference on Information Assurance and Security (IAS), IEEE 2015
[74] Ovidiu Vermesan, Peter Friess (eds.): Digitising the Industry, Internet of Things Connecting the Physical, Digital and Virtual Worlds, River Publishers Series in Communications, 2016
[75] Ala Al-Fuqaha, Mohsen Guizani, Mehdi Mohammadi, Mohammed Aledhari, Moussa Ayyash: Internet of Things: A Survey on Enabling Technologies, Protocols and Applications, IEEE Communications Surveys & Tutorials, Volume: 17, Issue: 4, 2015
[76] Arslan Munir, IFCIoT: Integrated Fog Cloud IoT Architectural Paradigm for the Future Internet of Things, IEEE Consumer Electronics Magazine, Vol. 6, Issue 3, July 2017
[77] Arslan Munir, IFCIoT: Integrated Fog Cloud IoT Architectural Paradigm for the Future Internet of Things, IEEE Consumer Electronics Magazine, Vol. 6, Issue 3, July 2017
[102] Internet of Things: Security Vulnerabilities and Challenges Ioannis Andrea, Chrysostomos Chrysostomou, George Hadjichristofi, The 3rd IEEE ISCC 2015 International Workshop on Smart City and Ubiquitous Computing Applications, https://doi.org/10.1109/ISCC.2015.7405513
[103] Rajan Arora, I2C Bus Pullup Resistor Calculation, Texas Instruments Application Report
[227] 11 Internet of Things (IoT) Protocols You Need to Know About, DesignSpark, https://www.rs-online.com/designspark/eleven-internet-of-things-iot-protocols-you-need-to-know-about
[246] Thread Stack Fundamentals, Thread group, 2015
[251] Jonas Olsson, „6LoWPAN demystified”, 2014, Texas Instruments
en/book.txt · Last modified: 2023/09/07 10:01 by 127.0.0.1
CC Attribution-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0