X10 Server - IOT device to leverage a collection of old X10 devices for home automation and lighting control.
Dependencies: IniManager mbed HTTPClient SWUpdate mbed-rtos Watchdog X10 SW_HTTPServer SW_String EthernetInterface TimeInterface SSDP
X10 Server
See the X10 Server Nodebook page
main.cpp
- Committer:
- WiredHome
- Date:
- 2019-02-27
- Revision:
- 7:16129d213e6a
- Parent:
- 6:80a97f156128
- Child:
- 8:a45fe77efcc5
File content as of revision 7:16129d213e6a:
// // X10 Server - Version 2 // /// If you've wired the CM17a to the same mbed pins, you should be able to simply /// install this application, and then install a properly configured ini file /// on the local file system. /// #include "mbed.h" // ver 120; mbed-rtos ver 111 #include "EthernetInterface.h" // ver 56 - https://os.mbed.com/users/WiredHome/code/EthernetInterface/ #include "SW_HTTPServer.h" // ver 58 #include "TimeInterface.h" // ver 25 #include "SWUpdate.h" // ver 26 #include "SW_String.h" // ver 2 #include "SSDP.h" // ver 7 #include "Watchdog.h" // ver 6 #include "IniManager.h" // v20 #include "X10.h" // v1 #include "X10Server.h" // v1 #include "WebPages.h" // Private handler for web queries #include "SignOfLife.h" // LED effects extern "C" void mbed_reset(); X10Interface cm17a(p5,p12); // cm17a.ParseCommand(buf); RawSerial pc(USBTX, USBRX); EthernetInterface eth; DigitalOut linkup(p26); DigitalOut linkdata(p25); Watchdog wd; bool WDEventOccurred = false; TimeInterface ntp(ð); time_t ntpSyncd; // time of the last sync bool ntpUpdateCheck = true; // scheduled check on startup time_t lastboottime; bool swUpdateCheck = true; // scheduled check on startup INI ini; LocalFileSystem local("local"); // some place to hold settings and maybe the static web pages const char * Server_Root = "/local"; // public for the WebPages handler to see // const char * BUILD_DATE = __DATE__ " " __TIME__; const char * PROG_NAME = "X10-Server-2"; char * My_Name = "X10-Server-2"; const char * My_SerialNum = "0000001"; int Server_Port = 80; // end public information const char * iniFile = "/local/X10svr.ini"; /// This function uses the settings in the .ini file to check for /// and install, a software update that might be available. /// void SoftwareUpdateCheck(bool force) { char url[100], name[10]; static time_t tstart = ntp.time(); time_t tNow = ntp.time(); //eth_mutex.lock(); #define ONE_DAY (24 * 60 * 60) if (force || (tNow - tstart) > ONE_DAY) { pc.printf("SoftwareUpdateCheck at %s (UTC)\r\n", ntp.ctime(&tNow)); tstart = tNow; swUpdateCheck = true; if (INI::INI_SUCCESS == ini.ReadString("SWUpdate", "url", url, sizeof(url)) && INI::INI_SUCCESS == ini.ReadString("SWUpdate", "name", name, sizeof(name))) { linkdata = true; pc.printf(" url: %s\r\n", url); pc.printf(" name: %s\r\n", name); SWUpdate_T su = SoftwareUpdate(url, name, DEFER_REBOOT); if (SWUP_OK == su) { pc.printf(" update installed, rebooting...\r\n"); Thread::wait(3000); mbed_reset(); } else if (SWUP_SAME_VER == su) { pc.printf(" no update available.\r\n"); swUpdateCheck = false; } else { pc.printf(" update failed %04X\r\n", su); Thread::wait(1000); } linkdata = false; } else { pc.printf(" can't get info from ini file.\r\n"); swUpdateCheck = false; } //eth_mutex.unlock(); } } /// This function syncs the node to a timeserver, if one /// is configured in the .ini file. /// void SyncToNTPServer(bool force) { char url[100]; char tzone[10]; char dstFlag[5]; // off,on,auto char dstStart[12]; // mm/dd,hh:mm char dstStop[12]; // mm/dd,hh:mm static time_t tlast = 0; time_t tnow = ntp.time(); if (((tnow - tlast) > (60*60*24)) || force) { printf("SyncToNTPServer\r\n"); if (INI::INI_SUCCESS == ini.ReadString("Clock", "timeserver", url, sizeof(url))) { ini.ReadString("Clock", "tzoffsetmin", tzone, sizeof(tzone), "0"); ini.ReadString("Clock", "dst", dstFlag, sizeof(dstFlag), "0"); ini.ReadString("Clock", "dststart", dstStart, sizeof(dstStart), ""); ini.ReadString("Clock", "dststop", dstStop, sizeof(dstStop), ""); printf("NTP update time from (%s)\r\n", url); int32_t tzo_min = atoi(tzone); if (strcmp(dstFlag,"on") == 0) { ntp.set_dst(1); } else if (strcmp(dstFlag, "off") == 0) { ntp.set_dst(0); } else /* if (strcmp(dstFlag, "auto") == 0) */ { ntp.set_dst(dstStart,dstStop); } ntp.set_tzo_min(tzo_min); int res = ntp.setTime(url); //printf(" NTP (release ethernet)\r\n"); if (res == 0) { time_t ctTime; ctTime = ntp.time(); ntpSyncd = ntp.get_timelastset();; tlast = ctTime; printf(" Time set to (UTC): %s\r\n", ntp.ctime(&ctTime)); printf(" ntpSyncd: %s\r\n", ntp.ctime(&ntpSyncd)); } else { ntpSyncd = 0; printf("Error return from setTime(%s) %d\r\n", url, res); } } else { ntpSyncd = 0; printf("no time server was set\r\n"); } } } /// This function monitors the USB serial interface for activity /// from a connected user. /// /// It offers a tiny bit of help if an unrecognized command is entered. /// void CheckConsoleInput(void) { static Timer timer; static bool test = false; static bool toggle = false; static char buf[80]; static int i; if (pc.readable()) { int c = pc.getc(); test = false; switch (c) { case 'r': mbed_reset(); break; case 's': swUpdateCheck = true; break; case 't': ntpUpdateCheck = true; break; case 'x': pc.printf("x10>"); i = 0; do { c = pc.getc(); pc.putc(c); if (c == '\x08') { // <bs> if (i < 0) { pc.printf("\r\n"); break; } } else if (c == '\r') { buf[i++] = '\0'; printf("Shell Command: '%s'\r\n", buf); cm17a.ParseCommand(buf); break; } else { buf[i++] = c; } } while(1); break; case 'z': pc.printf("X10 test mode enabled\r\n"); test = true; timer.start(); break; case '@': pc.printf("Sample '%s' file.\r\n", iniFile); pc.printf("[SWUpdate]\r\n"); pc.printf("url=http://192.168.1.201/mbed/\r\n"); pc.printf("name=X10svr\r\n"); pc.printf("[Clock]\r\n"); pc.printf("timeserver=time.nist.gov\r\n"); pc.printf("tzoffsetmin=-300\r\n"); pc.printf("[IP]\r\n"); pc.printf("ip=192.168.1.203\r\n"); pc.printf("nm=255.255.254.0\r\n"); pc.printf("gw=192.168.1.1\r\n"); pc.printf("[Node]\r\n"); pc.printf("id=X10Server-01\r\n"); pc.printf(";hint: Use either the fixed IP or the Node\r\n"); break; case '?': default: pc.printf("\r\n\r\n"); if (c > ' ' && c != '?') pc.printf("unknown command '%c'\r\n", c); pc.printf("IP: %s\r\n", eth.getIPAddress()); pc.printf(" Last Boot %s %s\r\n", ntp.ctime(&lastboottime), (WDEventOccurred) ? "(WD event)" : ""); pc.printf("\r\n"); pc.printf("Valid commands:\r\n"); pc.printf(" r - reset\r\n"); pc.printf(" s - software update\r\n"); pc.printf(" t - time server sync\r\n"); pc.printf(" x <House><Unit> <cmd> | x # | /XXXX\r\n"); pc.printf(" a-p House\r\n"); pc.printf(" 1 - 16 Unit\r\n"); pc.printf(" 1=On,0=Off,+#=Bright,-#=Dim (#=1 to 6)\r\n"); pc.printf(" ex: x a1 1 a3 +2\r\n"); pc.printf(" # = set baud rate\r\n"); pc.printf(" /XXXX send hex code XXXX\r\n"); pc.printf(" z - x10 test mode (toggles a1 on/off)\r\n"); pc.printf(" @ - Show a sample '%s' file.\r\n", iniFile); pc.printf("\r\n"); break; } } else { if (test) { if (timer.read_ms() > 1000) { timer.reset(); pc.printf(" Test Mode: Sending a1 %d\r\n", toggle); if (toggle) { cm17a.ParseCommand("a1 1"); } else { cm17a.ParseCommand("a1 0"); } toggle = !toggle; } } } } /// This handler is registered for callbacks from the X10server. /// /// It has only the simple responsibility of passing the command /// forward to the CM17a driver. As a useful side-effect, it /// blinks the Network interface data LED. /// void x10Handler(char * buffer, int size) { time_t ctTime; ctTime = ntp.time(); linkdata = true; pc.printf("X10 (%6s) %s (UTC)\r\n", buffer, ntp.ctime(&ctTime)); cm17a.ParseCommand(buffer); wait_ms(100); linkdata = false; } int main() { char ip[20],nm[20],gw[20]; pc.baud(460800); pc.printf("\r\n%s Build %s\r\n", PROG_NAME, BUILD_DATE); lastboottime = ntp.timelocal(); if (wd.WatchdogCausedReset()) { pc.printf("**** Watchdog Event caused reset at %s ****\r\n", ntp.ctime(&lastboottime)); WDEventOccurred = true; } wd.Configure(25); // very generous, but this is a network appliance, so a bit less deterministic. ini.SetFile(iniFile, 2); pc.printf("Initializing network interface...\r\n"); int initResult; if (INI::INI_SUCCESS == ini.ReadString("IP", "ip", ip, sizeof(ip)) && INI::INI_SUCCESS == ini.ReadString("IP", "nm", nm, sizeof(nm)) && INI::INI_SUCCESS == ini.ReadString("IP", "gw", gw, sizeof(gw))) { initResult = eth.init(ip,nm,gw); // use Fixed } else { initResult = eth.init(); // use DHCP } if (initResult) { // Failed to init ... pc.printf(" ... failed to initialize, rebooting...\r\n"); wait_ms(5000); mbed_reset(); } else { char * nn = (char *)malloc(33); if (!nn) error("no mem for network name"); ini.ReadString("Node", "id", nn, 32, "Name Me"); pc.printf("Name: %s\r\n", nn); eth.setName(nn); char * port = (char *)malloc(33); uint16_t portNum = 10630; // X10 Listener Port if (!port) error("no mem for port"); if (INI::INI_SUCCESS == ini.ReadString("IP", "port", port, sizeof(port))) portNum = atoi(port); do { pc.printf("Connecting to network...\r\n"); if (0 == eth.connect()) { wd.Service(); linkup = true; time_t tstart = ntp.time(); int speed = eth.get_connection_speed(); pc.printf("Ethernet Connected at: %d Mb/s\r\n", speed); pc.printf(" IP: %15s\r\n", eth.getIPAddress()); HTTPServer svr(Server_Port, Server_Root, 15, 30, 20, 50, &pc); svr.RegisterHandler("/", RootPage); svr.RegisterHandler("/info", InfoPage); svr.RegisterHandler("/software", SoftwarePage); svr.RegisterHandler("/setup.xml", Setup_xml); SSDP ssdp(My_Name, eth.getMACAddress(), eth.getIPAddress(), Server_Port); pc.printf(" X10 Server started %s (UTC)\r\n", ntp.ctime(&tstart)); X10Server x10svr(&x10Handler, portNum); while (eth.is_connected()) { static time_t tLastSec; wd.Service(); time_t tNow = ntp.timelocal(); CheckConsoleInput(); x10svr.poll(); svr.Poll(); // Web Server: non-blocking, but also not deterministic ShowSignOfLife(1); if (tNow != tLastSec) { pc.printf("time is %s\r\n", ntp.ctime(&tNow)); tLastSec = tNow; } SyncToNTPServer(ntpUpdateCheck); SoftwareUpdateCheck(swUpdateCheck); // Any other work can happen here // ... Thread::yield(); } linkup = false; linkdata = false; pc.printf("lost connection.\r\n"); eth.disconnect(); } else { pc.printf(" ... failed to connect.\r\n"); } } while (1); } }