Smart Home in 8 KBytes...

Smart Home in 8 KBytes...

Merry Xmas - the house of music with some IoT

2020. december 24. - Eaglewing

Merry Xmas!

...with this video first:

 

 

I integrated some Shellies for IoT control in last weeks. Shellys are small, wifi iOT devices based on ESP8266, which can help you to control the lights behind traditional switches, outlets, etc. Very similar to the Sonoff variants, bit it comes from EU (CE certified - important - imagine that you have a fire and your insurance company says you had some doubful chinese devices, so sorry...), and altough it is possible to control over cloud, that can be turned off (privacy matters), it is manageable over plain local LAN or over the popuplar smart home systems too (Domotiz, Home Assistant, and even Google Home etc) using web, MQTT, etc. Read more here about the API.

It looks like so in the wall, nicely hidden behind your original lamps switch:

Shelly 1 WiFi Switch - Bastelgarage Elektronik Online Shop


But how can you access it from your own application?
For example you may turn on/off a device with a simple command like this: 

http://192.168.0.55/relay/0?turn=on

You may ask about the security of this - and the question is very appropiate for every IoT device. These devices can be protected with username/password (and shouldl be!). 

The C# code as a MS Visual project file (it is based on work of Sebastian Leopold, on Instructables idea).

 

 can be downloaded from here

 

 

 

155 days and is still up....further ideas to implement

that is what I call stability and reliability:)

uptime_155days.JPG

 

Further ideas to develop/consider

- garage door control
- postbox check and notification
- fence perimeter control and event triggering
- doorbell ring detection and push notification
- water pressure monitoring (garden irrigation checkup)
- controlling (starting) the roomba 
- infrared appliance control (via a slave IR ESP unit)

 and so on...

How to survive the rollover problem - uptime passed 74 days.

Arduino has an internal timing rollover, which can cause bad things...if you are not aware of, read this:

https://playground.arduino.cc/Code/TimingRollover

The internal milli() function rolls over at every ~54 days.

Even better article: learn to live with and how to handle it well.

 

Well, systems works stable, without any problems:

74days.JPG

As alternative, you may use now() for timing purposes, but use it wisely. Depends what you want for. 

 

Backup UPS/battery for smart home

The best things are always the most simple things.

I added a UPS solution (as we have quite ofther power outages) to the smart home.

Well, connect a simple USB power bank to the unit (between the USB charger and the arduino unit), and you are done. Simple? Yes it is. Works? Yes, it does. 

I added a small code to detect if the system is running from battery - in that case the backlight of LCDs are cut off as they drain quite much power. 

 

powerbank.jpg

 

 

Controlling Wifi Outlets using ardiuno

I found some nice WiFi outlets on the market a while ago - produced by Kankun. kankun-wifi-smart-plug-socket-outlet-220v-eu-au-uk-kankun-small-k1-electrical-socket-to.jpg

Only disadvantage was that they come with US sockets - so EU adapter was needed. 

See more details here: http://www.anites.com/2015/01/hacking-kankun-smart-wifi-plug.html

Unit runs a simple stripped down linux. Here is the script which I installed on every unit after I was able to log in

https://github.com/kooiot/kankun

So now I am able to swich on-off them with simple http calls:

Example:

http://1.1.1.1/cgi-bin/relay.cgi?on

more later-----

wifisocket.JPG

 

 

 

The combined static/dynamic webpage response

...so how to have a combined, yet efficient webpage response to a request, which has static and dynamic content as well. 

So, the server webpages are created with the same logic as a PHP server would work.

It is based on the Arduino webserver, just modified the Webserver Example

A webserver static page (cropped) example from the SD card, example.htm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=2">

<title>Content1</title>

<style type="text/css">

.....

....

</script>
</head>
<body>

<h1>Ram</h1>
<div id="responseWin"> </div><br/>
$$$MYFREERAM$$$
<br/>
<input id="clickMe" type="button" value="Test 7" onclick="ajaxCaller(7);" />

</body>
</html>

 

It has a special token part, starting and ending with $$$.

So when the webserver founds a "token" while serving a static page from the SD card, it simply calls the appropiate function and sends the dynamit part of the webpage. After that it sends the remaining static part from the SD card..

memset(&tBuf,0,sizeof(tBuf));
clientCount = myFile.read(tBuf,63);
//check for token content $$$TOKEN$$$.
char* pch = strstr_P(tBuf,PSTR("$$$")); //first occurence
if (pch)
{
//found one occurence
char* pch2 = strstr_P(pch+3,PSTR("$$$")); //closing occurence
if (pch2!=NULL)
{

#ifdef ServerDEBUG
Serial.print(F("WebToken found:"));
#endif
strncpy(commandCodeText, pch+3, pch2-pch-3);
#ifdef ServerDEBUG
Serial.println(commandCodeText);
#endif
char SecBuf[65];
char ThrBuf[65];
memset(&SecBuf,0,sizeof(SecBuf));
strncpy(SecBuf,tBuf,pch-tBuf);
memset(&ThrBuf,0,sizeof(ThrBuf));
strcpy(ThrBuf,pch2+3);
client.write(SecBuf);
//executing conditional command
webTokeniser(client, commandCodeText);
client.write(ThrBuf);
clientCount = 0;

}

................

bool webTokeniser(EthernetClient thisClient, char recvcommandCodeText[])
{

if (strstr_P(recvcommandCodeText,PSTR("MYFREERAM")))
{

strcpy_P(tBuf,PSTR("<!--FREERAM TOKEN-->RAM:"));
thisClient.write(tBuf);
#ifdef ServerDEBUG
Serial.println(F("FREERAM TOKEN MODE"));
#endif
int currRam=freeRam();
//writing uptime too.
char upTimeStr[7];//="33d,23h";//uptime:33d,23h
getUpTime(upTimeStr);
sprintf (tBuf, "%d(%d)/8192, uptime:", currRam, minRam);
thisClient.write(tBuf);
thisClient.print(upTimeStr);
return true;
}

.......................

}

This was sent to the browser (cropped) as a response - the token part was discarded of course.

...........

</style>

</head>
<body>

<h1>SMART HOME</h1>
13:42:20 (CEST) 03/04/2018. RAM:1539(970)/8192, uptime:25d,17h
<span name="responseWin" id="responseWin" style="color:green;font-weight:bold"></span>

<!--

 .......................

and so on....have fun...

 

 

 

 

 

 

Move smarthome to the cloud

I made some brainstorming in the morning....what about moving the web service part of my smart home to the cloud?

 

smarthome_move_to_cloud.png

The arduino has a webserver, but it is very-very limited. Can handle only 4 network sockets, and the has no real multitasking capability (I overcomed these limitations, but Arduino was simply not designed for that). The "built" in webserver will still be there, as backup access.

This is just a quick brainstorming, but in the future I think I will realize it. I played a bit with Amazon and Google cloud services, and liked what I saw.

What is your opinion?

 

 

Wavin thermosystem - the ModBus TCP industrial connection with arduino

wtccontrol.jpg(draft article)

We have a nice thermal management system already at home. It was produced by Wavin

It controls every room separately, opening/closing all of the heating pipe valves (and can control the cooling in the summer too, which is really nice thing, believe me). 

Has a nice internet control option too if you have the WTC netbox module at home, (You may give a try with the demo user: http://www.mywavinhome.com/, user/password is wavintest / wavintest) 

With or without the netbox module, the whole system communicates (and thus can be controlled) using ModBus commands. Modbus is a serial protocol which is widely used by building/industrial management, etc.

wtc_net.PNG

 

 

It has a TCP variant implementation too - and luckily, the WTC-NET1 module supports that mode. So I don't have to cable from arduino to WTCNET1 directly, just enough to setup a tcp/ip client connection to the module and send/receive the WTC1 commands.

Some possible commands from the WTC1NET service manual:

wtc_modbus1.PNG

(Here is the whole service manual/full command list is interested)

Well, it sounds simple if you read this all together, getting the fact that it has errors (lot of analysis was required...). 

And the next thing that it is hard to find an arduino library for ModBus TCP.

I found one implementation here: http://myarduinoprojects.com/modbus.html but actually it was not working:

So, the first step was to fix the library....(I emailed the fixed version to the author to update on his webpage)

....then to get a good ModBus tester sofware to play and experience with the interface.

...then to intergrate this to Arduino:

thermos_display.png

 

(Article will be continued. And sorry for the typos, I am quit tired, that is the first draft of this entry. And this is just a hobby :) )

 

 

The Netscanner :)

The netscanner interface of the smart home is generic but important part. It would not be useful alone, but its support for the the other modules is very valuable (guess how do I know that all my IP Cams are online?).

What it is? To answer it very briefly: a service, which continuously pings (so scans) our LAN endlessly and saves what it saw. 

You think is not possible to write a netscanner using Arduino?Then waaaaaiit for it :). 

As a picture is worth more than thousand words:

netscanner_1.png

The Ping library does the hard stuff (Check it here: https://www.arduino.cc/en/tutorial/ping)

The Netscan function is called every 10 seconds, and then a ping is going to be executed. As this service is generally a blocking service (it has to wait for endpoint response, with an realistic but not to short timeout - and that is blocking), and we have a webserver running, some optimization was needed to be done - which is rather simple but effective: the netscanner will be only called, if there is no recent web activity. So the webserver (and thus the user experience) has always priority...

if (runEvery10Seconds())
{
 
if (!needsWebServerPriority()) 
{
#ifdef ServerDEBUG
Serial.print(F(" and web server is idle."));
#endif
ScanNetwork();
}

}

Let us have a look on the implementation of this function. Quite complex :)

First, we store only the very necessary information in RAM, as RAM is very precious.

  •  maximal 50 endpoints (when the table is full, the old entries will be deleted
  • MAC address 
  • IP Address, but only the last octet (I have only one subnet)
  • The Vendor name is loaded dynamically from the SD Card database (more about that later)

#define MAXHOST 50
#define PING_REQUEST_TIMEOUT_MS 50
#define PING_REQUEST_TIMEOUT_MS2 100

uint8_t mactable[6][MAXHOST];
uint8_t iplastbyte[MAXHOST];
unsigned long lastAliveTime[MAXHOST];

Ok, when we call the function, we set our target IP:

targetScanIP++; //begin with 1.
if (targetScanIP==255)
{
targetScanIP=1;
}

Then we check where we could store it

for (int k=0;k<MAXHOST;k++)
{
unsigned long temp=0;
//a legkiesebb idoflaget keressuk, az a legregibb.
if (temp>=lastAliveTime[k])
{
indexToStore = k;
temp = lastAliveTime[k];
}
}

Then we have to assemble our full local IP, and the full target IP.

Local IP is easy, as the Ethernet library already has a function for that.

Let use it for our advantage to assemble the target IP:

uint32_t loc_ipAddress = Ethernet.localIP();
uint8_t *ipPtr = (uint8_t*) &loc_ipAddress; //ipPtr[0], ipPtr[1], ipPtr[2], and ipPtr[3] a négy byte

IPAddress targetPingAddr(ipPtr[0],ipPtr[1],ipPtr[2],targetScanIP);

...do the scan:

ICMPPing::setTimeout(PING_REQUEST_TIMEOUT_MS);
ICMPEchoReply echoReply = ping(targetPingAddr, 1);

if (echoReply.status == SUCCESS)
{

Ok. That quite nice that we found something, let us check the received MAC. Maybe we already know it with different IP address. 

So we have to compare the echoReply.MACAddressSocket array against our uint8_t mactable[6][MAXHOST] "database"....

So much about the generic network scanner. I may post more details about it when needed.

Lets have a word about the web interface (there will be a whole blog entry about the webserver responses, as it has a "tokeniser" - a combined dynamic/static response capability).

The content of the right column is loaded all the time from the SD card. It won't be stored in RAM.
It is a standard text file, where names are paired with the appropriate MAC addresses.

9C:D6:43:5B:XX:XX,Router1
00:14:6C:21:XX:XX,Router2

So, when we come to display our detected endpoints, the last column will be loaded

bool resolveMACfromDB(char * macaddr, char * macName)
{

checkMinRam();
char readBuf[100];
char tBuf[30];
char macBuf[20];

byte readBufPos = 0;

strcpy(tBuf,macaddr);
strtoupper(tBuf);

if (strlen(macaddr)<6)
{
return false;
}

memset(macBuf,0,sizeof(macBuf));
memset(readBuf,0,sizeof(readBuf));

File dataFile = SD.open(F("endpoi.cfg"), FILE_READ);
if (dataFile)
{

while (dataFile.available())
{
char inputChar = dataFile.read();
if (inputChar != '\n')
{
if (inputChar != '\r')
{


readBuf[readBufPos]=inputChar;
readBufPos++;
if (readBufPos>99)
{
dataFile.close();
return false;//emergency exit
}
}
}
else
{


//process buf line
sscanf(readBuf, "%[^,],%s", macBuf, macName);
strtoupper(macBuf);
if (strlen(macBuf)>3)
{
if (strcmp(macBuf,tBuf)==0)
{
//match
// Serial.println(" found ok");
dataFile.close();
return true;
}
}

memset(readBuf,0,sizeof(readBuf));
memset(macBuf,0,sizeof(macBuf));
memset(macName,0,sizeof(macName));
readBufPos=0;
}
}

dataFile.close();
}
else
{
#ifdef ServerDEBUG
Serial.println(F("endpoi.cfg load failed."));
#endif
return false;
}
return false;

}

Of course, there will be endpoints we do not know, but some information would be handy there too to get.

There is a free service at MACVendors.com, which has a simply URL format to call:

http://api.macvendors.com/BC:4C:C4:1A

So for unknown endpoint, a html link to Macvendors.com will be generated. (That is the "Resolve" on the image above).

else
{
thisClient.print(F("<a target=\"_blank\" href=\"http://api.macvendors.com/"));
thisClient.print(macBuf);
thisClient.print(F("\">Resolve</a> "));
}

 

Meantime you may request my CV in email as I am looking for new challenges (job) - my main experience lies in the ICT project/program manager area for German multi companies. Contact is stingraycayman@gmail.com.  

süti beállítások módosítása