Computational Thinking Chain Reaction
(establishing a record with the Wolfram Language)

Pedro FONSECA & Diego ZVIOVICH

Join the Conversation #WolframTechConf

ABSTRACT.

Motivation

Tell me and I forget, teach me and I may remember, involve me and I learn.
												 Benjamin Franklin

Experimenting is by far the best way to learn, and Wolfram Language is the ideal tool for computational experimentation.

Real world interaction is key in motivating the next generation.

For this interaction, communication protocols are necessary. As the available possibilities allowing the connection of different technologies / devices with the Wolfram Language can be daunting, we want to give a flavour on how easy it is to perform fun experiments with the easiness of use of the Wolfram Language.

Making it easier for kids to have fun with computation thinking experimentation.

What a better way than to start with an example.

We will start with the following chain reaction:

IP camera live stream capture (RunProcess)

Wolfram WebImageSearch

Wolfram ImageIdentify

Wolfram Channel framework

Wolfram SendMessage

Tasker (Android app)

Wolfram DataDrop

Wolfram|Alpha

ZigBee

Arduino

Code image generation

YOU

IP camera setting

A phone camera has been set as an IP camera, transmitting a stream in continuous.

We will point this phone to an image

Search for an image of a cat.

cats=WebImageSearch["Cat"]

ImageResize[Import[cats[[1,4]]],400]

TaskRemove[obj];

Getting IP camera image and identifying it

A function to capture the live stream, running the ffmpeg process:
	(thanks to Arnoud for the example code)

CaptureMJPEGFromIPCamera[ mjpeg_String] := 
Module[{tmp, result,ffmpeg="X:\\02 TRAIN&TRIPS\\2018-10-16to19 - FUTURA - WTC2018\\ffmpeg-4.0.2-win64-static\\bin\\ffmpeg.exe"},
  tmp = CreateFile[CreateUUID[]<>".jpg"];
  RunProcess[{ffmpeg,"-f","MJPEG","-y","-i",mjpeg,"-r","1","-vframes", "1","-q:v","1", tmp}];
  result = Import[tmp];
  DeleteFile[tmp];
  result
  ]

A dynamic import of the image:

obj=SessionSubmit[ScheduledTask[tempIPCAMERAimage=CaptureMJPEGFromIPCamera["http://admin:admin@192.168.10.105:1080/mjpeg"],0.5]];

Dynamic[tempIPCAMERAimage]

Identification of the image

entityAux=ImageIdentify[tempIPCAMERAimage]

animal=entityAux[(EntityProperty["name", EntityProperty["Concept","Name"], "EntityProperty[\"Concept\", \"Name\"]"])]

Sending identified animal to Mathematica on Raspberry Pi (R1).

We will send to a Mathematica session on a Raspberry Pi the animal name, through the Channel Framework:

ChannelSend["wtc2018", animal]

GOTO Raspberry Pi

Forwarding SMS text to Wolfram Datadrop via Tasker.

ImageCapture[]

Tasker is an application for Android which performs tasks (sets of actions) based on contexts (application, time, date, location, event, gesture) in user-defined profiles or in clickable or timer home screen widgets.
This functionality can also be performed with IFTTT which allows connectivity between all kinds of devices, including Android and iPhones and applications. IFTTT also allows to post to Wolfram Datadrop™.

Tasker was set to forward this crucial message to a Databin

Reads databin and searches for information in Wolfra|Alpha

Our application will call for the datadrop universal data accumulator and extract the text that was posted by the android phone via tasker. Mathematica will search for additional information that was curated in Wolfram/Alpha™.

bin=Databin[];

word=bin["Latest"]["Data"]["text"]

Column[{WolframAlpha[word,{{"Image:SpeciesData",1},"Content"}],WolframAlpha[word,{{"ScientificName:SpeciesData",1},"Content"}]},Alignment->Center]

scientificName=WolframAlpha[word,{{"ScientificName:SpeciesData",1},"Plaintext"}]

Sending Wolfram/Alpha™ to Arduino via Zigbee.

And to finish, let’s all visualise the scientific name, in a visual form of telex Morse code.

Arduino Setup

The arduino code can be found here.

Define function

letters=CharacterRange["A","Z"];
figures={"-","?",":","🐺","3","%","@","$","8",";","(",")",".",",","9","0","1","4","'","5","7","=","2","/","6","+",""(*FromCharacterCode[13]*),""(*FromCharacterCode[10]*),"α","#"," "};
codes={2^^11,2^^11001,2^^1110,2^^1001,2^^1,2^^1101,2^^11010,2^^10100,2^^110,2^^1011,2^^1111,2^^10010,2^^11100,2^^1100,2^^11000,2^^10110,2^^10111,2^^1010,2^^101,2^^10000,2^^111,2^^11110,2^^10011,2^^11101,2^^10101,2^^10001,2^^01000,2^^10,2^^11111,2^^11011,2^^100};
morseSymbols=Thread[Rule[figures,Take[codes,Length@figures]]];
morseRules=Join[Thread[Rule[ToLowerCase@letters,Take[codes,Length@letters]]],Thread[Rule[letters,Take[codes,Length@letters]]],morseSymbols];

sendMorseCode[number_Integer]:=DeviceWrite[comPort,Join[ToCharacterCode@ToString@number,{10}]]
insertChangeOfSet[string_]:=Module[{characters=Characters[ToUpperCase[string]],iLetterQ,booleList},
booleList=Differences[Boole[LetterQ/@characters]];
booleList=Join[{If[LetterQ[First[characters]],1,-1]},booleList];
booleList=booleList/.{1->"α",-1->"#"};
characters=Insert[characters,"a",Position[booleList,"α"]];
booleList=Differences[Boole[LetterQ/@characters]];
booleList=Join[{If[LetterQ[First[characters]],1,-1]},booleList]/.{1->"α",-1->"#"};
characters=Insert[characters,"#",Position[booleList,"#"]]/."a"->"α"]
sendMorseString[string_]:=Module[{message=(insertChangeOfSet[string]/.morseRules)},Table[sendMorseCode[message[[i]]];Pause[0.25],{i,Length@message}]]

Transmit text

comPort=DeviceOpen["Serial",{"COM12","BaudRate"->9600}]

ImageCapture[]

Pause[1];sendMorseString[scientificName];

And the purpose was!

Pushing the envelope of internet cat images Computational Thinking!!!!

For the reference, code on Raspberry Pi

With the following, we connect to the channel, and pull the last entry.

CloudConnect[]

listener=ChannelListen["wtc2018"]

auxValue=listener["Message"]

And now we send the animal name to a Wolfram Cloud API function.

URLExecute["https://www.wolframcloud.com/objects/6d91fdeb-d74d-4fb4-a543-cba1b7a62fe3?text="<>URLEncode[auxValue]]

Coming back to some basics - IoT with the Wolfram Language

Besides all the business potential, that is a topic by itself for a conference as a whole... we will be looking at the interaction fun part of it (and leave the business model dreams to you!).

The market is full of devices, some difficult to access to, other extremely easy, working as web servers (with webpages), others simply sending data to web servers, etc. We will be using our own Things, and the internet to interact with them.

Things can be equipped with simple processors, that just run the same routine over and over again. We will look into the Arduino and the ESP family of controllers.

Things can also be equipped with processors ready to run complete OS (similar to a PC, or better, a smart phone). A cheap computer that can serve a purpose of a Thing is the Raspberry Pi, as it is a low power device, passive cooled (silent), and equipped with lots of input and output ports, for easy interaction with sensors, cameras, etc.

NOTE: this notebook interacts with the above mentioned devices and databases,  specifically preset for this presentation; by itself, the notebook is not self contained, and will not show the forecasted results unless the whole environment is also set up.

Mathematica ↔ Python ↔ UDP ↔ ESP8266.

We start immediately with an impossible example: UDP

Mathematica has no UDP connectivity, but, no issues, we can actually call the help of python or JavaScript, and external evaluate the UDP part.

ImageCapture[]

session=StartExternalSession["Python"];

ExternalEvaluate[session,File["X:\\02 TRAIN&TRIPS\\2018-10-16to19 - FUTURA - WTC2018\\Presentations\\udpexchange.py"]];

Just for the reference, here below the python script:

(*ReadByteArray["X:\\02 TRAIN&TRIPS\\2018-10-16to19 - FUTURA - WTC2018\\Presentations\\udpexchange.py"]*)
tmp=CreateFile[
FileNameJoin[{NotebookDirectory[],"udpexchange.py"}]];
BinaryWrite[tmp,ByteArray[]];Close[tmp];

Read value of the soil moisture sensor:

v=ExternalEvaluate[session,"udpexchange('192.168.10.140',4210,bytes(1))"]

data={};

Dynamic[Pause[.7];
ListPlot[data=Join[data,{ExternalEvaluate[session,"udpexchange('192.168.10.140',4210,bytes(1))"]}],Joined->True,PlotTheme->"Detailed"]]

ESP8266 -> Wolfram Data Drop-> Mathematica

The following is easier, we will just post readings of temperature and humidity to a datadrop, and read them back to Mathematica.

zobj=SessionSubmit[ScheduledTask[ts=TimeSeries[Databin["ygNb5jHM"]],15]];

Dynamic[
GraphicsRow[{DateListPlot[Module[{data=data=Normal@ts["DHT22Hum"]},data[[All,2]]=ToExpression[data[[All,2]]];
TimeSeriesWindow[TimeSeries[data],{DatePlus[Now,Quantity[-30,"Minutes"]],Now}]],PlotTheme->"Detailed",FrameLabel->{None,"Humidity (%)"},PlotLabel->"Humidity Reading - ESP8266",PlotRange->All],DateListPlot[Module[{data=data=Normal@ts["DHT22Temp"]},data[[All,2]]=ToExpression[data[[All,2]]];
TimeSeriesWindow[TimeSeries[data],{DatePlus[Now,Quantity[-30,"Minutes"]],Now}]],PlotTheme->"Detailed",FrameLabel->{None,"Deg. Celsius"},PlotLabel->"Temperature Reading - ESP8266",PlotRange->All]},ImageSize->Full],TrackedSymbols:>{ts}]

TaskRemove[zobj];

Serial wired protocol
Mathematica ↔  Arduino

Although extremely interesting, this is not the most practical solution for the IoT world, that is typically expecting wireless solution...

Let’s start by connecting a light intensity sensor (well... already plugged in):

Load the Wolfram Sketch into the Arduino, to add the software layer of the connection (a customized Firmata based protocol).

(*in case I missed the port*)
SystemOpen[]

(*the first time, it has to download the Arduino IDE, taking a few minutes*)
arduino=DeviceOpen["Arduino","COM10"]

And read the information from the Arduino port:

Dynamic[DeviceRead[arduino,"A0"],UpdateInterval->0.1,TrackedSymbols:>{}]

Dynamic[ListLinePlot[If[Length[#]<50,#,#[[-50;;]]]&@AppendTo[pinVals,DeviceRead[arduino,"A0"]],PlotRange->{0,5}],Initialization:>(pinVals={})]

(the brighter the environment, the lower the voltage value)

We can see how it reacts by pointing the phone light...

Now, let’s send an order to Arduino to manipulate the voltage of three outputs, linked to a 3 color RGB based LED.

Configure the ports:

DeviceConfigure[arduino,<|9->"AnalogOutput",10->"AnalogOutput",11->"AnalogOutput"|>];

For instant, activate Red, followed by Blue:

DeviceWrite[arduino,<|10->(Quantity[5, "V", volts, "Volts"]),9->(Quantity[0, "V", volts, "Volts"]),11->(Quantity[0, "V", volts, "Volts"])|>];

DeviceWrite[arduino,<|10->(Quantity[0, "V", volts, "Volts"]),9->(Quantity[0, "V", volts, "Volts"]),11->(Quantity[1, "V", volts, "Volts"])|>];

Or more fun...:

ColorSlider[Dynamic[x,(DeviceWrite[arduino,<|9->(Quantity[5 #1[[3]], "V", volts, "Volts"]),10->(Quantity[5 #1[[1]], "V", volts, "Volts"]),11->(Quantity[5 #1[[2]], "V", volts, "Volts"])|>];x=#)&],AutoAction->True,ImageSize->Large,AppearanceElements->"Spectrum"]

It would actually be interested to map the sensibility of this light intensity sensor on the RGB space (well... under very bad lab conditions, a far from perfect light source, totally non calibrated, etc..). But again, fun!

auxInt=Table[{r,g,b,r+g+b,DeviceWrite[arduino,<|10->(Quantity[r, "V", volts, "Volts"]),9->(Quantity[g, "V", volts, "Volts"]),11->(Quantity[b, "V", volts, "Volts"])|>];Pause[0.05];DeviceRead[arduino,"A0"]},{r,0,5},{g,0,5},{b,0,5}];

ListDensityPlot3D[Flatten[auxInt,2][[All,{1,2,3,5}]],AxesLabel->{"R","G","B"}]

Clearly needing some opacity scaling calibration, but doesn’t it look to have a weaker sensibility on green? (it most likely depends hugely on the room lightnening...)

But this was wired...

Mathematica (laptop) -> Serial (~Firmata) -> Arduino

WR has a similar connected devices pre-built solution available, based on Arduino Yún (which has integrated WIFI), and very cool!
-> WTC2015 : Maker Devices - In Depth Beginner Workshop

(*DeviceClose[arduino]*)

ZMQ protocol
Mathematica -> Node-RED

ZMQ is a very practical, simple, but quite complete communication protocol, typically working over TCP, that should be quite practical on the IoT world.

I say should because, unfortunately, there's not a lot of IoT ready solutions / packages available with/for this protocol.

Mathematica understands ZMQ quite well, as it is a fundamental technology for some under the hood information exchange.

Node-RED is kind of a protocol connector software, very practical to un-mess the world of the communication protocols. Instead of Node-RED, we could have used any of the many existing web services for the IoT world, including services that provide Node-RED...

So, we will demonstrate a communication between Mathematica and Node-RED, installed on a Raspberry Pi. To look into Raspberry , we are using a VNC connection.

Mathematica (laptop) -> IP/TCP/ZMQ -> Internet -> Node-RED (on Raspberry)

I haven’t fixed any IP address for this presentation (I probably should have...), and so, thirst I start by defining the different addresses (using, for instance, the Advanced Network Scanner software to get the IP’s):

SystemOpen[]

pcIP="192.168.137.174";
raspberryIP="192.168.137.242";

espLaserIP="192.168.137.210"
(*name: ESP_103C4A.mshome.net, A0:20:A6:10:3C:4A*);

espRotaryIP="192.168.137.203"
(*name: ESP_04C556.mshome.net, A0:20:A6:04:C5:56*);

(*espPushButton: ESP_2CC2BD.mshome.net, 5C:CF:7F:2C:C2:BD*)

Now we take a look at the Raspberry... (well, there’s nothing there..., as Node-RED has a web based interface).

So, let’s look at that interface, using the browser (on this laptop)

SystemOpen[URLBuild[<|"Scheme"->"http","Domain"->raspberryIP,"Port"->"1880"|>]]

(the standard port that Node-RED server web interface is listening to)

We see that I have already set a listener. We now send a message using ZMQ protocol to this connection technology, and see what happens:

clientNodeRed=SocketConnect[IPAddress[raspberryIP<>":9999"],"ZMQ"];

WriteString[clientNodeRed,DateString[]]

On the Node-RED side, I haven’t set it to do fancy stuff, just a direct output to a message board...

Of course that this could be set in both ways, as is presented on some examples of the documentation.

MQTT protocol
Node-RED -> Mathematica

Much more practical is to dialog with the MQTT protocol. A broker based TCP protocol used considerably by the IoT community.

We need a broker, that will manage the message dispatching. There are many services available on the web, but we can also install and launch our one (e.g. Mosquitto).

Node-RED (on Raspberry) -> IP/TCP/MQTT -> Internet -> Mosquitto broker (automatically set by Mathematica, on the laptop ! ) -> Mathematica on the laptop

Mathematica doesn’t know about MQTT. But it actually does have a library on its installation directory, that is good enough for some cases. Very interesting is that it also has the Mosquitto broker available!

Get@"MQTTLink`"

aBrooker=StartBroker[];
imAclient=CreateClient[];
imAclient=CreateClient[];

aBrooker

We subscribe to the topic, on the brooker we just launched.

TopicSubscribe[imAclient,"aTopic","MessageReceivedFunction"->((returnedMessage=ByteArrayToString["Data"//.Normal[#3]])&)];

And we wait for messages.

Dynamic[returnedMessage,UpdateInterval->1]

(that in this example, are coming from the Node-RED environment: let’s look at it)

TopicUnsubscribe[imAclient,"aTopic"];

MQTT protocol
ESP8266 -> broker -> Mathematica

We just click on the button, and we get a message back!
(example tutorial: https://techtutorialsx.com/2017/04/09/esp8266-connecting-to-mqtt-broker/)
(ESP MQTT library: https://github.com/knolleary/pubsubclient)

ESP8266 -> IP/TCP/MQTT -> Internet -> Mosquitto broker (automatically set by Mathematica, on the laptop) -> Mathematica on the laptop

TopicSubscribe[imAclient,"bTopic","MessageReceivedFunction"->((returnedMessageB=ByteArrayToString["Data"//.Normal[#3]])&)];

Dynamic[returnedMessageB,UpdateInterval->1]

taskAux=SessionSubmit[ScheduledTask[returnedMessageB="nothing going on...",3]];

TaskRemove[taskAux];

TopicUnsubscribe[imAclient,"bTopic"];

Sketch:

(*ReadByteArray["sketch_push_button.ino"]*)
tmp=CreateFile[
FileNameJoin[{NotebookDirectory[],"docs.zip"}]];
BinaryWrite[tmp,ByteArray[]];Close[tmp];

HTTP GET 
Raspberry (or other  client) -> Mathematica (server)

Asking Mathematica (laptop) for an information:

Browser or any HTTP device ready, acting as client IP/TCP/HTTP GET -> Internet -> Mathematica (as a server), returning HTML body

We know that we can create webpages with the Wolfram Language. We can profit from this server capabilities to send information back upon request by a client. Here we are sending a HTML page back as the response to all clients contacting Mathematica, but it could be any other HTTP body information type:

taskBux=SessionSubmit[ScheduledTask[reading=DeviceRead[arduino,"A0"],1]]

listenerBux=SocketListen[pcIP<>":80",Function[{assoc},
With[{client=assoc["SourceSocket"],
html="<html>
<body>
<h1>Hello from the Wolfram Language</h1>
Your reading is:  "<>ToString[reading]<>"
</body>
</html>"},
WriteString[client,"HTTP/1.1 200 OK
Date: "<>DateString[]<>" "<>$TimeZoneEntity["ShortName"]<>"
Content-Type: text/html
Content-Length: "<>ToString[StringLength[html]]<>"\n\n"<>html<>"\n\n"
];
Close[client]
]
]
]

SystemOpen[URLBuild[<|"Scheme"->"http","Domain"->First[listenerBux["Socket"]["DestinationIPAddress"]],"Port"->listenerBux["Socket"]["DestinationPort"]|>]]

Of course that we can ask for exactly the same information, but from a different device, or even another Mathematica session, like from the Raspberry Pi environment.

TaskRemove[taskBux];

serverZux=listenerBux["Socket"];DeleteObject[listenerBux];Close[serverZux];Clear[serverZux];

HTTP PUT (and post, get, etc.)
Mathematica (client) -> ESP8266 (server)

This time:

instead of requesting information, we send (the information holder is the client, sending it to a server);

instead of sending to Mathematica, it is Mathematica who sends it, as a client (by our decision), to a web server installed on a tiny ESP8266;

in this example,  the information we send is actually an order to turn on and off a laser.

Yves gave a presentation yesterday where he used this protocol to control a robot!
“Educational Robotics with the Wolfram Language”

We have a laser connected to a ESP8266, where a library was loaded.
Library source: http://mancusoa74.blogspot.fr/2018/02/simple-http-rest-server-on-esp8266.html just changed basic configuration, like access to internet.

Mathematica -> IP/TCP/HTTP PUT (json body) -> Internet -> ESP8266 operating as a REST style server

The first command we can execute is a HTTP GET of the server’s root

URLExecute[espLaserIP]

And the web server on the ESP8266 answered. We could have done exactly the same request with the browser:

SystemOpen[URLBuild[<|"Scheme"->"http","Domain"->espLaserIP,"Port"->"80"|>]]

Now, we set the ESP server to set an object (a thing specific to this library I used...), that we can manipulate, turning it on and off.

URLRead[espLaserIP<>"/leds"]

As you can see the HTTP code 204 is provided back meaning that the resource is neither available nor is empty.

Now let’s create the resource by providing a JSON body as part of the HTTP POST.

URLExecute[HTTPRequest[espLaserIP<>"/leds",<|"Method"->"POST","ContentType"->"application/json","Body"->ExportString[<|"id"->1,"gpio"->12,"status"->0|>,"RawJSON"]|>]];

Now we query again the resource through GET and this time server replies with 200 and with the JSON representation of the resource.

URLRead[espLaserIP<>"/leds"]

And we create a simple functions to turn on and off the laser, with simple PUT:

turnLightOn:=URLExecute[HTTPRequest[espLaserIP<>"/leds",<|"Method"->"PUT","ContentType"->"application/json","Body"->ExportString[<|"id"->1,"gpio"->12,"status"->1|>,"RawJSON"]|>]]
turnLightOff:=URLExecute[HTTPRequest[espLaserIP<>"/leds",<|"Method"->"PUT","ContentType"->"application/json","Body"->ExportString[<|"id"->1,"gpio"->12,"status"->0|>,"RawJSON"]|>]]

And we send a sequence of orders to the ESP:

Do[turnLightOn;Pause[0.5];turnLightOff;Pause[0.5],{10}]

Even without the Pause, it would be a bit slow..., but we are running a HTTP server on a ESP8266... with a few mA of power consumption (we have a Raspberry and 3 ESP8266 running from a 2A USB power bank...)

HTTP Raspberry Mathematica  ->  Mathematica (laptop) HTTP -> ESP8266

And now, much funnier.

The purpose is still to turn on and off the laser, but the order comes from another Thing, the Raspberry Pi (both browser or Mathematica), not directly to the ESP8266, but instead to Mathematica on the laptop, and this last sends the order to the ESP server.

Mathematica or web browser (as client on Raspberry)-> IP/TCP/HTTP GET (returning a simple HTML body response) -> Internet -> Mathematica (as server on laptop) -> IP/TCP/HTTP PUT (json body) -> ESP8266 operating as a REST style server

We start by setting up the Mathematica laptop listening service, that will reply with a sympathy acknowledge HTML body

listenerCux=SocketListen[pcIP<>":64120",
Function[{assoc},
Module[{html,client,value},
(*Print[assoc["Data"]];*)
client=assoc["SourceSocket"];
value="value"/.ImportString[assoc["Data"], "HTTPRequest"]["Query"];
(*Print[value];*)
auxValue=value;
html="<html><body><h1>thank you for your input</h1>Your chosen value is:  "<>ToString[value]<>"</body></html>";
WriteString[client,"HTTP/1.1 200 OK
Date: "<>DateString[]<>" "<>$TimeZoneEntity["ShortName"]<>"
Content-Type: text/html
Content-Length: "<>ToString[StringLength[html]]<>"\n\n"<>html<>"\n\n"
];
Close[client];
]
]];

Now, we could open the browser, and we send the information from the laptop browser to Mathematica

SystemOpen[URLBuild[<|"Scheme"->"http","Domain"->First[listenerCux["Socket"]["DestinationIPAddress"]],"Port"->listenerCux["Socket"]["DestinationPort"]|>]]

But we are doing that from the Raspberry Pi Mathematica.

Turning it on and off from Raspberry pi Mathematica?

objAux=SessionSubmit[ScheduledTask[Which[auxValue=="on",turnLightOn,auxValue=="off",turnLightOff],1]];

TaskRemove[objAux];

serverZux=listenerCux["Socket"];DeleteObject[listenerCux];Close[serverZux];Clear[serverZux];

ESP8266 (SQL) -> Database (cloud) -> Mathematica

An ESP8266 is currently reading a rotary encoder, and sending the information to a database (directly through SQL, instead of passing through a PHP REST service or similar).

Let's get this information in real time:

Needs["DatabaseLink`"]

conn=OpenSQLConnection[JDBC["MySQL(Connector/J)","109.71.40.11:3306/pfonseca_db_tests1"],"Username"->"pfonseca_user1","Password"->"user1Pass3$"]

Yes, I will change my password after the presentation...

Dynamic[data=SQLSelect[conn,"our_table2"]/.{SQLDateTime->DateObject};DateListPlot[data[[All,{2,3}]],PlotRange->All],UpdateInterval->1]

Well... not really real time... something is buggy on my esp8266 sketch... next time!

SQLExecute[conn,"TRUNCATE TABLE "<>"our_table2"];

CloseSQLConnection[conn];

What is missing

Well... A LOT!!

-> CoAOP, etc., that the WL doesn't know about (yet...), but that becomes relatively easy to extend with the JavaLink or JavaScript and python external evaluate.

-> and much much more...
	LoRa / GPRS / bluetooth, etc.

The sky is the limit

Literally, as you can easily send a weather balloon with a Raspberry Pi running Mathematica, and have the Thing Interne-ting from above.

(unless you are like Elon Musk, and in that case, the space is your final frontier)