Alexa can speak with iot devices (bulbs, switches, …) directly without creating any skill. Recently I’ve discoverer the library fauxmoesp to use a ESP32 as a virtual device and use it with Alexa.
I want to create a simple example with my M5Stack (it’s basically one ESP32 with an screen). It’s pretty straightforward to do it.
Here the firmware that I deploy to my device
Then I only need to pair my virtual device (in this case “my device”) to my compatible Alexa Device (In my case one echo spot). We can do it with the Alexa device’s menu or simply saying “Alexa discover devices”. Then I’ve got my iot device paired to my Alexa and I can say something like: “Alexa, switch on my device”, “Alexa, switch off my device” or “Alexa, set my device at 50%”
Today I want to keep on with the previous example. This time I want to create one Alexa skill that listen to external events. The example that I’ve build is the following one:
I’ve got one ESP32 with a proximity sensor (one HC-SR04) that is sending the distance captured each 500ms to a MQTT broker (a mosquitto server).
I say “Alexa, use event demo” then Alexa tell me to put my hand close to the sensor and it will tell me the distance.
The process is similar to the previous example. There’s a GadgetInterceptor to find the endpointId of my Raspberry Pi. But now the Raspberry Pi must emit to the event to the skill, not the skill to the event. Now my rpi’s python script is a little bit more complicated. We need to start the main loop for the Alexa Gadget SDK but also we need to start another loop listening to a mqtt event. We need to use threads as we see in a previous post. That’s the script that I’m using.
We’re keeping on playing with Alexa. This time I want to create one skill that uses a compatible device (for example one Raspberry Pi 3). Here you can see the documentation and examples that the people of Alexa provides us. Basically we need to create a new Product/Alexa gadget in the console. It gives us one amazonId and alexaGadgetSecret.
Then we need to install the SDK in our Raspberry Pi (it install several python libraries). The we can create our gadget script running on our Raspberry Pi, using the amazonId and alexaGadgetSecret. We can listen to several Alexa events. For example: When we say the wakeword, with a timer, an alarm and also we can create our custom events.
I’m going to create one skill that allows me to say something like: “Alexa, use device demo and set color to green” or “Alexa, use device demo and set color to red” and I’ll put this color in the led matrix that I’ve got (one Raspberry Pi Sense Hat)
This is the python script:
import logging
import sys
import json
from agt import AlexaGadget
from sense_hat import SenseHat
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
logger = logging.getLogger(__name__)
sense = SenseHat()
sense.clear()
RED = [255, 0, 0]
GREEN = [0, 255, 0]
BLUE = [0, 0, 255]
YELLOW = [255, 255, 0]
BLACK = [0, 0, 0]
class Gadget(AlexaGadget):
def on_alexa_gadget_statelistener_stateupdate(self, directive):
for state in directive.payload.states:
if state.name == 'wakeword':
sense.clear()
if state.value == 'active':
sense.show_message("Alexa", text_colour=BLUE)
def set_color_to(self, color):
sense.set_pixels(([color] * 64))
def on_custom_gonzalo123_setcolor(self, directive):
payload = json.loads(directive.payload.decode("utf-8"))
color = payload['color']
if color == 'red':
logger.info('turn on RED display')
self.set_color_to(RED)
if color == 'green':
logger.info('turn on GREEN display')
self.set_color_to(GREEN)
if color == 'yellow':
logger.info('turn on YELLOW display')
self.set_color_to(YELLOW)
if color == 'black':
logger.info('turn on YELLOW display')
self.set_color_to(BLACK)
if __name__ == '__main__':
Gadget().main()
One important thing here is the GadgetInterceptor. This interceptor find the endpointId from the request and append it to the session. This endpoint exists because we’ve created our Product/Alexa gadget and also the python script is running on the device. If the endpoint isn’t present maybe our skill must say to the user something like “No gadgets found. Please try again after connecting your gadget.”.
Today I want to play with Grafana. Let me show you my idea:
I’ve got a Beewi temperature sensor. I’ve been playing with it previously. Today I want to show the temperature within a Grafana dashboard.
I want to play also with openweathermap API.
Fist I want to retrieve the temperature from Beewi device. I’ve got a node script that connects via Bluetooth to the device using noble library.
I only need to pass the sensor mac address and I obtain a JSON with the current temperature
#!/usr/bin/env node
noble = require('noble');
var status = false;
var address = process.argv[2];
if (!address) {
console.log('Usage "./reader.py <sensor mac address>"');
process.exit();
}
function hexToInt(hex) {
var num, maxVal;
if (hex.length % 2 !== 0) {
hex = "0" + hex;
}
num = parseInt(hex, 16);
maxVal = Math.pow(2, hex.length / 2 * 8);
if (num > maxVal / 2 - 1) {
num = num - maxVal;
}
return num;
}
noble.on('stateChange', function(state) {
status = (state === 'poweredOn');
});
noble.on('discover', function(peripheral) {
if (peripheral.address == address) {
var data = peripheral.advertisement.manufacturerData.toString('hex');
out = {
temperature: parseFloat(hexToInt(data.substr(10, 2)+data.substr(8, 2))/10).toFixed(1)
};
console.log(JSON.stringify(out))
noble.stopScanning();
process.exit();
}
});
noble.on('scanStop', function() {
noble.stopScanning();
});
setTimeout(function() {
noble.stopScanning();
noble.startScanning();
}, 2000);
setTimeout(function() {
noble.stopScanning();
process.exit()
}, 20000);
And finally another script (this time a Python script) to collect data from openweathermap API, collect data from node script and storing the information in a influxdb database.
I’m running this python script from a Raspberry Pi3 with a Sense Hat. Sense Hat has a atmospheric pressure sensor, so I will also retrieve the pressure from the Sense Hat.
From openweathermap I will obtain:
Current temperature/humidity and atmospheric pressure in the street
UV Index (the measure of the level of UV radiation)
Weather conditions (if it’s raining or not)
Weather forecast
I run this script with the Rasberry Pi crontab each 5 minutes. That means that I’ve got a fancy time series ready to be shown with grafana.
I must admit this post is just an excuse to play with Grafana and InfluxDb. InfluxDB is a cool database especially designed to work with time series. Grafana is one open source tool for time series analytics. I want to build a simple prototype. The idea is:
One Arduino device (esp32) emits a MQTT event to a mosquitto server. I’ll use a potentiometer to emulate one sensor (Imagine here, for example, a temperature sensor instead of potentiometer). I’ve used this circuit before in another projects
One Python script will be listening to the MQTT event in my Raspberry Pi and it will persist the value to InfluxDB database
I will monitor the state of the time series given by the potentiometer with Grafana
I will create one alert in Grafana (for example when the average value within 10 seconds is above a threshold) and I will trigger a webhook when the alert changes its state
One microservice (a Python Flask server) will be listening to the webhook and it will emit a MQTT event depending on the state
Another Arduino device (one NodeMcu in this case) will be listening to this MQTT event and it will activate a LED. Red one if the alert is ON and green one if the alert is OFF
The server
As I said before we’ll need three servers:
MQTT server (mosquitto)
InfluxDB server
Grafana server
We’ll use Docker. I’ve got a Docker host running in a Raspberry Pi3. The Raspberry Pi is a ARM device so we need docker images for this architecture.
ESP32
The Esp32 part is very simple. We only need to connect our potentiometer to the Esp32. The potentiometer has three pins: Gnd, Signal and Vcc. For signal we’ll use the pin 32.
We only need to configure our Wifi network, connect to our MQTT server and emit the potentiometer value within each loop.
The esp32 emits an event (“/pot”) with the value of the potentiometer. So we’re going to create a MQTT listener that listen to MQTT and persits the value to InfluxDB.
Grafana
In grafana we need to do two things. First to create one datasource from our InfluxDB server. It’s pretty straightforward to it.
Finally we’ll create a dashboard. We only have one time-serie with the value of the potentiometer. I must admit that my dasboard has a lot things that I’ve created only for fun.
Thats the query that I’m using to plot the main graph
SELECT
last("value") FROM "pot"
WHERE
time >= now() - 5m
GROUP BY
time($interval) fill(previous)
Here we can see the dashboard
And here my alert configuration:
I’ve also created a notification channel with a webhook. Grafana will use this web hook to notify when the state of alert changes
Webhook listener
Grafana will emit a webhook, so we’ll need an REST endpoint to collect the webhook calls. I normally use PHP/Lumen to create REST servers but in this project I’ll use Python and Flask.
We need to handle HTTP Basic Auth and emmit a MQTT event. MQTT is a very simple protocol but it has one very nice feature that fits like hat fits like a glove here. Le me explain it:
Imagine that we’ve got our system up and running and the state is “ok”. Now we connect one device (for example one big red/green lights). Since the “ok” event was fired before we connect the lights, our green light will not be switch on. We need to wait util “alert” event if we want to see any light. That’s not cool.
MQTT allows us to “retain” messages. That means that we can emit messages with “retain” flag to one topic and when we connect one device later to this topic, it will receive the message. Here it’s exactly what we need.
from flask import Flask
from flask import request
from flask_httpauth import HTTPBasicAuth
import paho.mqtt.client as mqtt
import json
client = mqtt.Client()
app = Flask(__name__)
auth = HTTPBasicAuth()
# http basic auth credentials
users = {
"user": "password"
}
@auth.get_password
def get_pw(username):
if username in users:
return users.get(username)
return None
@app.route('/alert', methods=['POST'])
@auth.login_required
def alert():
client.connect("docker", 1883, 60)
data = json.loads(request.data.decode('utf-8'))
if data['state'] == 'alerting':
client.publish(topic="/alert", payload="1", retain=True)
elif data['state'] == 'ok':
client.publish(topic="/alert", payload="0", retain=True)
client.disconnect()
return "ok"
if __name__ == "__main__":
app.run(host='0.0.0.0')
Nodemcu
Finally the Nodemcu. This part is similar than the esp32 one. Our leds are in pins 4 and 5. We also need to configure the Wifi and connect to to MQTT server. Nodemcu and esp32 are similar devices but not the same. For example we need to use different libraries to connect to the wifi.
This device will be listening to the MQTT event and trigger on led or another depending on the state
#include <PubSubClient.h>
#include <ESP8266WiFi.h>
const int ledRed = 4;
const int ledGreen = 5;
// Wifi configuration
const char* ssid = "my_wifi_ssid";
const char* password = "my_wifi_password";
// mqtt configuration
const char* server = "192.168.1.111";
const char* topic = "/alert";
const char* clientName = "com.gonzalo123.nodemcu";
int value;
int percent;
String payload;
WiFiClient wifiClient;
PubSubClient client(wifiClient);
void wifiConnect() {
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("WiFi connected.");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void mqttReConnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect(clientName)) {
Serial.println("connected");
client.subscribe(topic);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
String data;
for (int i = 0; i < length; i++) {
data += (char)payload[i];
}
cleanLeds();
int value = data.toInt();
switch (value) {
case 1:
digitalWrite(ledRed, HIGH);
break;
case 0:
digitalWrite(ledGreen, HIGH);
break;
}
Serial.print("] value:");
Serial.println((int) value);
}
void cleanLeds() {
digitalWrite(ledRed, LOW);
digitalWrite(ledGreen, LOW);
}
void setup() {
Serial.begin(9600);
pinMode(ledRed, OUTPUT);
pinMode(ledGreen, OUTPUT);
cleanLeds();
Serial.println("start");
wifiConnect();
client.setServer(server, 1883);
client.setCallback(callback);
delay(1500);
}
void loop() {
Serial.print(".");
if (!client.connected()) {
mqttReConnect();
}
client.loop();
delay(500);
}
One saturday morning I was having a breakfast and I discovered face_recognition project. I started to play with the opencv example. I put my picture and, Wow! It works like a charm. It’s pretty straightforward to detect my face and also I can obtain the face landmarks. One of the landmark that I can get is the nose tip. Playing with this script I realized that with the nose tip I can determine the position of the face. I can see if my face is align to the center or if I move it to one side. As well as I have a new iot device (one ESP32) I wanted to do something with it. For example control a servo (SG90) and moving it from left to right depending on my face position.
First we have the main python script. With this script I detect my face, the nose tip and the position of my face. With this position I will emit an event to a mqtt broker (a mosquitto server running on my laptop).
import face_recognition
import cv2
import numpy as np
import math
import paho.mqtt.client as mqtt
video_capture = cv2.VideoCapture(0)
gonzalo_image = face_recognition.load_image_file("gonzalo.png")
gonzalo_face_encoding = face_recognition.face_encodings(gonzalo_image)[0]
known_face_encodings = [
gonzalo_face_encoding
]
known_face_names = [
"Gonzalo"
]
RED = (0, 0, 255)
GREEN = (0, 255, 0)
BLUE = (255, 0, 0)
face_locations = []
face_encodings = []
face_names = []
process_this_frame = True
status = ''
labelColor = GREEN
client = mqtt.Client()
client.connect("localhost", 1883, 60)
while True:
ret, frame = video_capture.read()
# Resize frame of video to 1/4 size for faster face recognition processing
small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
# Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
rgb_small_frame = small_frame[:, :, ::-1]
face_locations = face_recognition.face_locations(rgb_small_frame)
face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)
face_landmarks_list = face_recognition.face_landmarks(rgb_small_frame, face_locations)
face_names = []
for face_encoding, face_landmarks in zip(face_encodings, face_landmarks_list):
matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
name = "Unknown"
if True in matches:
first_match_index = matches.index(True)
name = known_face_names[first_match_index]
nose_tip = face_landmarks['nose_tip']
maxLandmark = max(nose_tip)
minLandmark = min(nose_tip)
diff = math.fabs(maxLandmark[1] - minLandmark[1])
if diff < 2:
status = "center"
labelColor = BLUE
client.publish("/face/{}/center".format(name), "1")
elif maxLandmark[1] > minLandmark[1]:
status = ">>>>"
labelColor = RED
client.publish("/face/{}/left".format(name), "1")
else:
status = "<<<<"
client.publish("/face/{}/right".format(name), "1")
labelColor = RED
shape = np.array(face_landmarks['nose_bridge'], np.int32)
cv2.polylines(frame, [shape.reshape((-1, 1, 2)) * 4], True, (0, 255, 255))
cv2.fillPoly(frame, [shape.reshape((-1, 1, 2)) * 4], GREEN)
face_names.append("{} {}".format(name, status))
for (top, right, bottom, left), name in zip(face_locations, face_names):
# Scale back up face locations since the frame we detected in was scaled to 1/4 size
top *= 4
right *= 4
bottom *= 4
left *= 4
if 'Unknown' not in name.split(' '):
cv2.rectangle(frame, (left, top), (right, bottom), labelColor, 2)
cv2.rectangle(frame, (left, bottom - 35), (right, bottom), labelColor, cv2.FILLED)
cv2.putText(frame, name, (left + 6, bottom - 6), cv2.FONT_HERSHEY_DUPLEX, 1.0, (255, 255, 255), 1)
else:
cv2.rectangle(frame, (left, top), (right, bottom), BLUE, 2)
cv2.imshow('Video', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
video_capture.release()
cv2.destroyAllWindows()
Now another Python script will be listening to mqtt events and it will trigger one event with the position of the servo. I know that this second Python script maybe is unnecessary. We can move its logic to esp32 and main opencv script, but I was playing with mqtt and I wanted to decouple it a little bit.
The event was one kind of Hackathon where a group of people meet together one day, to share our side projects and to work together (yes. We also have a lunch and beers also :). The format of the event is just a copy of the event that our colleagues from Bilbao called “El Comité“.
@ibaiimaz spoke about one project to create one collaborative pomodoro where the people of one team can share their status and see the status of the rest of the team. When I heard pomodoro and status I immediately thought in one servo moving a flag and some LEDs turning on and off. We had a project. @penniath and @tatai also joined us. We also had a team.
We had a project and we also had a deadline. We must show a working prototype at the end of the day. That means that we didn’t have too many time. First we decided the mockup of the project, reducing the initial scope (more ambitious) to fit it within our time slot. We discuss intensely for 10 minutes and finally we describe an ultra detailed blueprint. That’s the full blueprint of the project:
It was time to start working.
@penniath and @tatai worked in the Backend. It must be the responsible of the pomodoro timers, listen to MQTT events and create an API for the frontend. The backend also must provide a WebSockets interface to allow real time events within the frontend. They decided to use node and socket.io for the WebSockets. You can see the source code here.
@ibaiimaz started with the frontend. He decided to create an Angular web application listening to socket.io events to show the status of the pomodoro. You can see the source code here.
Finaly I worked with the hardware. I created a prototype with one ESP32, two RGB LEDs, one button, one servo and a couple of resistors.
That’s the source code.
#include <WiFi.h>
#include <PubSubClient.h>
int redPin_g = 19;
int greenPin_g = 17;
int bluePin_g = 18;
int redPin_i = 21;
int greenPin_i = 2;
int bluePin_i = 4;
#define SERVO_PIN 16
const int buttonPin = 15;
int buttonState = 0;
int channel = 1;
int hz = 50;
int depth = 16;
const char* ssid = "SSID";
const char* password = "password";
const char* server = "192.168.1.105";
const char* topic = "/pomodoro/+";
const char* clientName = "com.gonzalo123.esp32";
WiFiClient wifiClient;
PubSubClient client(wifiClient);
void wifiConnect() {
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print("*");
}
Serial.print("WiFi connected: ");
Serial.println(WiFi.localIP());
}
void mqttReConnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect(clientName)) {
Serial.println("connected");
client.subscribe(topic);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
String data;
for (int i = 0; i < length; i++) {
data += (char)payload[i];
}
int value = data.toInt();
if (strcmp(topic, "/pomodoro/gonzalo") == 0) {
Serial.print("[gonzalo]");
switch (value) {
case 1:
ledcWrite(1, 3400);
setColor_g(0, 255, 0);
break;
case 2:
setColor_g(255, 0, 0);
break;
case 3:
ledcWrite(1, 6400);
setColor_g(0, 0, 255);
break;
}
} else {
Serial.print("[ibai]");
switch (value) {
case 1:
setColor_i(0, 255, 0);
break;
case 2:
setColor_i(255, 0, 0);
break;
case 3:
setColor_i(0, 0, 255); // green
break;
}
}
Serial.print("] value:");
Serial.println(data);
}
void setup()
{
Serial.begin(115200);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(redPin_g, OUTPUT);
pinMode(greenPin_g, OUTPUT);
pinMode(bluePin_g, OUTPUT);
pinMode(redPin_i, OUTPUT);
pinMode(greenPin_i, OUTPUT);
pinMode(bluePin_i, OUTPUT);
ledcSetup(channel, hz, depth);
ledcAttachPin(SERVO_PIN, channel);
wifiConnect();
client.setServer(server, 1883);
client.setCallback(callback);
delay(1500);
}
void mqttEmit(String topic, String value)
{
client.publish((char*) topic.c_str(), (char*) value.c_str());
}
void loop()
{
if (!client.connected()) {
mqttReConnect();
}
client.loop();
buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
mqttEmit("/start/gonzalo", (String) "3");
}
delay(200);
}
void setColor_i(int red, int green, int blue)
{
digitalWrite(redPin_i, red);
digitalWrite(greenPin_i, green);
digitalWrite(bluePin_i, blue);
}
void setColor_g(int red, int green, int blue)
{
digitalWrite(redPin_g, red);
digitalWrite(greenPin_g, green);
digitalWrite(bluePin_g, blue);
}
The MQTT server (a mosquitto server) was initially running in my laptop but as well as I had one Raspberry Pi Zero also in my bag we decided to user the Pi Zero as a server and run mosquitto MQTT server with Raspbian. Everything is better with a Raspberry Pi. @tatai helped me to set up the server.
Here you can see the prototype in action
That’s the kind of side projects that I normally create alone but definitely it’s more fun to do it with other colleagues even it I need to wake up early one Saturday morning.
I’ve been playing with MQTT in previous posts. Today I want to build a simple dashboard. Basically because I’ve got a 3.5inch display for my Raspberry Py and I want to use it. The idea is set up my Rasperry Pi as a web kiosk and display the MQTT variables in real time using websockets. Let’s start.
Set up Raspberry Pi as a web kiosk is pretty straightforward. You only need to follow instructions detailed here. Now we will prepare the MQTT inputs. Today we’re going to reuse one example of previous post. A potentiometer controlled by a nodemcu microcontroller connected to our MQTT server via Wifi.
We also will build another circuit using a Arduino board and a ethernet Shield.
With this circuit we’ll register the temperature (using a LM35 temperature sensor), a photo resistor (CDS) to show the light level and a relay to switch on/off a light bulb. The Idea of the circuit is emit the temperature and light level to mosquitto mqtt server and listen to switch status form mqtt server to fire the relay. That’s the arduino code
Now we’re going to work with dashboard. This days I’m working with OpenUI5 within various projects and because of that we’ll use this library to build the dashboard. we’ll build something like this:
Nowadays I’m involved with a cloud project based on SAP Cloud Platform (SCP). Side projects are the best way to mastering new technologies (at least for me) so I want to build something with SCP and my Arduino stuff. SCP comes whit one IoT module. In fact every cloud platforms have, in one way or another, one IoT module (Amazon, Azure, …). With SCP the IoT module it’s just a Hana Database where we can push our IoT values and we’re able to retrieve information via oData (the common way in SAP world).
It’s pretty straightforward to configure the IoT module with the SAP Cloud Platform Cockpit (Every thing can be done with a hana trial account).
NodeMcu
First I’m going to use a simple circuit with my NodeMcu connected to my wifi network. The prototype is a potentiometer connected to the analog input. I normally use this this circuit because I can change the value just changing the potentiometer wheel. I know it’s not very usefull, but we can easily change it and use a sensor (temperature, humidity, light, …)
It will send the percentage (from 0 to 100) of the position of the potentiometer directly to the cloud.
SAP Cloud Platform allows us to create web applications using SAPUI5 framework easily. It also allows us to create a destination (the way that SAP’s cloud uses to connect different modules) to our IoT module. Also every Hana table can be accessed via oData so and we can retrieve the information easily within SAPIUI5.
onAfterRendering: function () {
var model = this.model;
this.getView().getModel().read("/my-hana-table-odata-uri", {
urlParameters: {
$top: 1,
$orderby: "G_CREATED desc"
},
success: function (oData) {
model.setProperty("/value", oData.results[0].C_VALUE);
}
});
}
The web application (with SCP and SAPUI5) can access to IoT values via oData. We can fetch data again and again, but that’s not cool. We want real time updates in the web application. So we need WebSockets. SCP IoT module allows us to use WebSockets to put information, but not get updates (afaik. Let me know if I’m wrong). We also can connect our IoT to an existing MQTT server, but in this prototype I only want to use websockets. So we’re going to create a simple WebSocket server with node and socket.io. This server will be listening to devices updates (again and again with a setInterval function via oData) and when it detects a change it will emit a broadcast to the WebSocket.
SAP’s SCP also allows us to create services with Cloud Foundry. So we’ll create our nodejs server there.
In another post we spoke about NFC tag readers and Arduino. Today I’ll do the same but with a Raspberry Pi. Why? More or less everything we can do with an Arduino board we can do it also with a Raspberry Pi (and viceversa). Sometimes Arduino is to much low level for me. For example if we want to connect an Arduino to the LAN we need to set up mac address by hand. We can do it but this operation is trivial with Raspberry Pi and Python. We can connect our Arduino to a PostgreSQL Database, but it’s not pretty straightforward. Mi background is also better with Python than C++, so I feel more confortable working with Raspberry Pi. I’m not saying that RPi is better than Arduino. With Arduino for example we don’t need to worry about start proceses, reboots and those kind of thing stuff that we need to worry about with computers. Arduino a Raspberry Pi are different tools. Sometimes it will be better one and sometimes the other.