{"id":20165467,"url":"https://github.com/tabahi/tabahiconsole","last_synced_at":"2026-02-12T06:33:06.341Z","repository":{"id":46589391,"uuid":"479820128","full_name":"tabahi/TabahiConsole","owner":"tabahi","description":"tabahi.tech IoT cloud library for Arduino devices, ESP32, and ESP8266","archived":false,"fork":false,"pushed_at":"2025-02-26T13:59:11.000Z","size":5663,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-10T00:53:26.231Z","etag":null,"topics":["arduino","esp32","esp8266","iot","iot-cloud","wifi"],"latest_commit_sha":null,"homepage":"https://tabahi.tech","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tabahi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-09T19:08:04.000Z","updated_at":"2025-02-26T13:59:14.000Z","dependencies_parsed_at":"2024-09-18T03:20:54.520Z","dependency_job_id":"76a04a5b-6880-4e7c-a5c2-97f05f826996","html_url":"https://github.com/tabahi/TabahiConsole","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tabahi%2FTabahiConsole","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tabahi%2FTabahiConsole/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tabahi%2FTabahiConsole/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tabahi%2FTabahiConsole/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tabahi","download_url":"https://codeload.github.com/tabahi/TabahiConsole/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248137995,"owners_count":21053775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","esp32","esp8266","iot","iot-cloud","wifi"],"created_at":"2024-11-14T00:37:55.336Z","updated_at":"2026-02-12T06:33:06.334Z","avatar_url":"https://github.com/tabahi.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tabahi Console (_Dead_)\n\n![Version](https://img.shields.io/static/v1?label=v\u0026message=1.4\u0026color=success)\n![Device](https://img.shields.io/static/v1?label=Device\u0026message=ESP32\u0026color=blueviolet)\n![Device](https://img.shields.io/static/v1?label=Device\u0026message=ESP8266\u0026color=blueviolet)\n![Device](https://img.shields.io/static/v1?label=Device\u0026message=Arduino\u0026color=blueviolet)\n![Language](https://img.shields.io/github/languages/top/tabahi/TabahiConsole)\n\n\nRemotely control, view, interconnect and update your ESP32 and ESP8266. An arduino Library for ESP32 and ESP8266 cloud IoT interface. \n[https://tabahi.tech](https://tabahi.tech/)\n\n\n- OTA update compiled binary over the internet from [console.tabahi.tech](https://console.tabahi.tech/).\n- Change or view synchronized variables from the web.\n- Push data to cloud and view it nicely on the [console.tabahi.tech](https://console.tabahi.tech/).\n- Uses simple yet secure TCP channel. Doesn't use HTTPS to save resources.\n- An independent UDP monitor to emulate Serial Monitor remotely.\n- Get notified on telegram on variables changes.\n- Connect different devices with each other over the internet using messages.\n- Run scripts (JS) on the cloud to avoid burdening the microcontroller.\n- Currently the trial is limited to 10000 writes. Email me at tabahi@duck.com to increase the limit (for free).\n- Scalable and customizable cloud source on license.\n\n\n## Initializing\n\nStart by initializing the class\n\n`TTC Console(TTC_server, TTC_port, UDP_port, USER_TOKEN, USER_SECRET, DEBUG_TTC);`\nthen call the following during `setup()`:\n\n\n```cpp\n#include \u003cTabahiConsole.h\u003e\n\nconst char* ssid     = \"WiFi Name\";\nconst char* password = \"WiFi Pass\";\n\n#define TTC_server \"api.tabahi.tech\" //api.tabahi.tech\n#define USER_TOKEN \"6223338df64144aac74a3622\" //copy from account\n#define USER_SECRET \"Deu9DqvSS6pbNuIoI43aCh\" //copy from account\n#define DEBUG_TTC 1 //set to 1 to print verbose info on Serial\n\nTTC Console(TTC_server, 2096, 44561, USER_TOKEN, USER_SECRET, DEBUG_TTC);\n\nString mac_address = \"\"; //will set in setup. Used for node identification.\n\nvoid setup()\n{\n  Serial.begin(115200);\n  WiFi.disconnect(true); // delete old wifi config\n  delay(1000);\n  WiFi.begin(ssid, password);\n\n  mac_address = WiFi.macAddress();\n  Serial.print(F(\"\\nMAC: \"));\n  Serial.println(mac_address);\n\n  Console.initialize();\n  /* Or reinitialize with different settings:\n  Console.initialize(const char *TTC_server, int TTC_port, int UDP_port, const char *USER_TOKEN, const char *USER_SECRET, bool enable_debug);\n  */\n}\n```\n\n\n\n## Device Identification\n\nEach device is assigned a Node Token by the console. Token can be generated manually on the console and pre-configured, or the device can use any unique ID such as MAC address to identify itself and get a Node Token (NT).\n\nAutomatically registering a new NT using the `mac_address` or any unique identifier so that you can differentiate one device from another:\n\n```cpp\nif (WiFi.status() == WL_CONNECTED)\n{\n  if (Console.node_token_valid == false) //Need to get a Node Token before anything else\n  {\n    WiFiClient TCPclient;\n\n    //Identify Node Token using mac address\n    //int Identify(TCPClientObj *TCPclient, String mac_id_str)\n    int idn_status = Console.Identify(\u0026TCPclient, mac_address);\n\n    if (idn_status == 1) {\n      Serial.print(\"Got NT: \");\n      Serial.println(Console.NT);\n    }\n    else {\n      Serial.printf(\"Identification failed %d\\n\", idn_status);\n    }\n  }\n}\n```\n\nIf you already added a device manually on the web console then set the assigned Node Token as:\n\n```cpp\nConsole.set_NODE_TOKEN(\"61800000000000000000000\"); //see the Configure tab for NT after adding a device\n```\n\nThe mac address is the easiest identifier you can read and use for differentiating devices without changing the code for each device. It's not necessarily required by the console. If two of your devices are using the same identifier then the console will consider them as a single device. Once a Node Token is assigned then the mac address (or unique ID) isn't needed for variables and data syncing. \n\nUDP monitor doesn't use Node Token, instead it uses any String name you pass during `Console.CommitLogs(\"name_for_UDP_monitor\");`. We use the mac address to make sure that each device shows up with a different name.\n\n## Syncing\n\nSyncing is performed over a secure TCP protocol. It first sends the values from the device's memory, then fetches the latest values from the console cloud. Optionally, you can also trigger a script on console after the sync.\n\n\n```cpp\nWiFiClient TCPclient;\nint n_vars = Console.runSync(\u0026TCPclient); //returns \u003e=0 number of variables if no error\n\n\nif (n_vars \u003e= 0) //check if variables sync was ok\n{\n  Console.logln(\"Sync: OK\");\n  Console.printVariables(); //print all the vairables on Serial\n}\nelse\n  Serial.printf(\"Sync failed, status: %d \\n\", n_vars);\n\n\n//parse the synced variables\nunsigned long heartbeat = Console.get_ulong(\"heartbeat\");\n\n//check if a there is a bool variable 'example'\nif (Console.isValidType(\"example\", 'b'))\n  bool example = Console.get_bool(\"example\");\n\n//setting a variable to a new value\nint new_var = 123;\nConsole.set_int(\"new_var\", new_var);\n//this new value will show on the console after the next sync cycle\n\n```\n\nIn the case of a conflict, the value from the console is preferred. But if the value changes on both sides, on the console AND on the device between two syncs, then the value from the device is preferred unless the variable is set to constant. There is a 2 minutes grace period for the console to yield when the device changes to a new value while the user has recently updated a new value in console. In other words, the new value that the user has set on the console will be discarded after 2 minutes because the device keeps on updating to a newer value. To avoid this, don't update the value on the console if the device is programmed to always set to a newer value between two syncs.\n\nTo trigger a script on the cloud after the sync use:\n\n```cpp\n  WiFiClient TCPclient;\n  int n_vars = runSync(\u0026TCPclient, \"script_token\", \"arg1=abcd, arg2=123\");\n```\n\nwhere `script_token` is a 24 character token assigned to the script on the console. And `args` are the arguments to be passed to the script. e.g., `val=ABC0000000, a=123`. Leave empty as `\"\"` if there are no arguments.\n\n\n\n## Data Logging\n\nData is inserted into the tables using a timestamp. Timestamp is created when `Console.newDataRow()` is called. Unlike synced variables, `Console.push_` functions are a one-way traffic. The device gets an acknowledgement upon the successful data insertion but it can't read back the data. The limit of data rows is less than variable writes on the console but users can delete the old data on the console to make space for the newer data.\n\nExample usage for `push_float`,\n`push_int`,\n`push_long`,\n`push_ulong`,\n`push_String`:\n\n```cpp\n  //create a data row:\n  Console.newDataRow(); //new row with a current timestamp\n  Console.push_int(\"integer\",  99);\n  Console.push_ulong(\"a\",  123);\n  Console.push_float(\"b\", 456.789);\n  Console.push_String(\"c_geo\", String(\"1.223\") + \",\" + String(\"-5.235\"));  //headings including 'geo' will link to google maps\n\n  WiFiClient TCPclient;\n  //Data is not sent until 'CommitData' is called.\n  if (Console.CommitData(\u0026TCPclient) \u003e 0) Console.logln(\"Data sent\");\n```\n\nAlternatively you can push data in JSON format with a single command:\n\n```cpp\nConsole.SendJSON(\u0026TCPclient, \"{\\\"row1\\\":{\\\"data1\\\": 90, \\\"data2\\\": 0.211, \\\"data3\\\": \\\"chill\\\"}, \\\"time\\\":\" + String(Console.realtime()) + \"}\");\n\n\nConsole.SendJSON(\u0026TCPclient, \"{\\\"row2\\\":{\\\"data1\\\": 80, \\\"data2\\\": 0.456, \\\"data3\\\": \\\"pill\\\"}}\"); //server adds it's own timestamp if no epoch 'time' is given.\n//Don't make rows too big to handle for memory\n//No need to CommitData after SendJSON\n\n```\n\n\n## Intra-Node Communication\nSmall messages (300 chars) can be sent from one device to another via the console cloud. The `runSync` also checks the number of messages in the inbox for a device:\n\n```cpp\nint n_vars = Console.runSync(\u0026TCPclient);\n\nif (n_vars \u003e= 0) //check if variables sync was ok\n{\n  Serial.printf(\"Inbox count: %d \\n\", Console.inbox);\n}\n\n```\n\nThen  read all the messages one-by-one in either top-to-bottom or from bottom-to-top manner\n\n```cpp\nwhile (Console.inbox \u003e 0)\n{\n  String readMsg = Console.readMessage(\u0026TCPclient, 't'); //'t' from top, 'b' from bottom\n  /*\n  Message format example:\n  \u003e12,6253dc60b1fdc14832d2c6ee,the message content\n\n  */\n  //parse time, sender and the content of the message:\n  if (readMsg[0] == '\u003e')\n  {\n    int comma_index = readMsg.indexOf(',');\n    unsigned long seconds_ago = atol(readMsg.substring(1, comma_index).c_str());\n    readMsg = readMsg.substring(comma_index + 1); comma_index = readMsg.indexOf(',');\n    String sender_NT = readMsg.substring(0, comma_index);\n    String message = readMsg.substring(comma_index + 1);\n\n    Serial.print(\"Recieved: \"); Serial.print(seconds_ago);\n    Serial.print(\" seconds ago, from \"); Serial.println(sender_NT);\n    Serial.print(\"Msg:\"); Serial.println(message);\n  }\n  else\n  {\n    Serial.print(\"Error:\"); Serial.println(readMsg);\n  }\n  Console.inbox--;\n}\n```\n\nTo send a message to other devices, use their NT or identifier as\n\n```cpp\n//Send message to others (@ mac_address, or use Node Token Console.NT or identifier)\nint sendMsgStatus = Console.sendMessage(\u0026TCPclient, mac_address, \"yo, sup?\");\nSerial.print(\"Msg sent:\\t\"); Serial.println(sendMsgStatus); //1 = sent\n```\n\n\n## UDP Monitor\n\nUDP monitor uses a simpler, faster, protocol without any encryption to deliver debugging or informational logs to the console. The mechanism for UDP only requires the correct `USER_TOKEN` to work, therefore can be used to debug other more sophisticated functionalities in case of failure.\n\n```cpp\n//print some logs on UDP monitor:\nConsole.log(\"UTC time: \"); //will show on UDP monitor on console.tabahi.tech\nConsole.logln(Console.realtime());\nConsole.logln(\"This is an information\");\nConsole.CommitLogs(mac_address.c_str()); //send all the monitor logs to server, using mac address as the identifier\n//must call CommitLogs() after log() to send logs to the server.\n```\n\nThe identifier for UDP monitor can be anything but it helps to keep devices separate from each other by using a unique hardware identifier such as MAC address. `log()` and `logln()` functions store the data in a temporary buffer which is sent to the console on `CommitLogs()`.\n\nNote: Avoid sending critical information such as `USER_TOKEN` over the UDP Monitor because it's not encrypted. Whereas, all other features such as `runSync, CommitData and sendMessage` are encrypted for increased security.\n\n\n## Weather\n\nYou can get the weather forecast and sun timing using the Latitude and Longitude for a location. The API uses the data provided by met.no, which provide data for local times around the globe.\n\n\n```cpp\n\nvoid checkWeatherForecast()\n{\n  //Should define at top of SettingsTTC.h :\n  //#define WEATHER_HOURS_MAX 12\n  //Then pass parameters as:\n  String geo_lat = \"60.362\";\n  String geo_lon = \"5.013\";\n  int forecast_hours = 12;\n\n  WiFiClient TCPclient;\n  uint8_t hours_reported = Console.fetchWeather(\u0026TCPclient, geo_lat, geo_lon, forecast_hours);\n  Console.log(F(\"Got forecast for hr:\\t\")); Console.logln(hours_reported);\n\n  for (int i = 0; i \u003c forecast_hours; i++)\n  {\n    if (Console.forecast[i].temp != INVALID_VALUE)\n    {\n      Serial.print(\"Hour=\");\n      Serial.print(i);\n      Serial.print('\\t');\n      Serial.print(Console.forecast[i].temp);\n      Serial.print('\\t');\n      Serial.print(Console.forecast[i].humid);\n      Serial.print('\\t');\n      Serial.print(Console.forecast[i].perc);\n      Serial.print('\\t');\n      Serial.println(Console.forecast[i].symbol);\n    }\n  }\n\n}\n```\n\nThe forecast data for `WEATHER_HOURS_MAX` hours is held in the memory for later use, so that the device doesn't have to check the forecast again and again. The decrease or increase the number of forecast hours, edit the first line of `SettingsTTC.h`.\n\n\n## OTA Update\n\nA compiled binary `.bin` can be uploaded on the console for each device with a new version name for the device to check if their version is different therefore an update should be executed. In the following example, first `fetchUpdateURL` is used to get the link of the binary which will include the version name (if update available) in it. If the link doesn't include the version name of the currently running binary then the update is started using the new binary link with `executeOTAupdate`.\n\n```cpp\n#define myVersion \"version1\" //version name for this binary\n\nvoid try_update()\n{\n  WiFiClient TCPclient;\n  String latest_bin_link = Console.fetchUpdateURL(\u0026TCPclient, TTC_server, \"\");\n  //update server can be different (should be HTTP), the last argument is for identifier if NODE_TOKEN is not known\n\n  if (latest_bin_link == \"0\") Console.logln(\"No update\");\n  else if (latest_bin_link == \"ERROR\") Console.logln(latest_bin_link);\n  else if (latest_bin_link.length() \u003e 0)\n  {\n    if (latest_bin_link.indexOf(myVersion) == -1)  //URL doesn't contains the current version name\n    {\n      Console.log(\"New version: \");\n      Console.logln(latest_bin_link);\n      Console.logln(\"Updating\");\n      Console.CommitLogs(mac_id.c_str());\n\n      if (!Console.executeOTAupdate(latest_bin_link))\n        Console.logln(\"Update Failed\");\n    }\n    else Console.logln(\"Latest version\");\n  }\n}\n```\nSee example `OTAupdate.ino` for updating with a safe mode in case the updated version keeps on restarting.\n\n\n\n\n## TTC Console Class\n\nUse all this functions with the class prefix. e.g., `Console.get_int`\n### Variables\n\n#### Variable: int\n```cpp\nint16_t get_int(char *key_name);\nbool set_int(char *key_name, int16_t val);\n```\n\n#### Variable: float\n```cpp\nfloat get_float(char *key_name);\nbool set_float(char *key_name, float val);\n```\n\n#### Variable: long\n```cpp\nlong get_long(char *key_name);\nbool set_long(char *key_name, long val);\n```\n\n#### Variable: ulong\n```cpp\nunsigned long get_ulong(char *key_name);\nbool set_ulong(char *key_name, unsigned long val);\n```\n\n#### Variable: bool\n```cpp\nbool get_bool(char *key_name);\nbool set_bool(char *key_name, bool val);\n```\n\n#### Variable: String\n```cpp\nString get_String(char *key_name);\nbool set_String(char *key_name, String val);\n\n//or use a pointer:\nbool set_String(char *key_name, String *val);\n```\n\n#### Variable: time\n```cpp\n//unsigned long UTC epoch in seconds\nunsigned long get_time(char *key_name);\nbool set_time(char *key_name, unsigned long val);\n```\n\n\n#### Variable: hex\n```cpp\nbyte *get_hex(char *key_name); //returns the pointer of the array\n\nbool set_hex(char *key_name, byte val[], uint16_t len);\n```\n\n\n#### Variable: geo-coordinates (lat, long)\n```cpp\nbool set_geo(char *key_name, String val_lat, String val_lon);\n\nString get_geo_lat(char *key_name);\nString get_geo_lon(char *key_name);\n```\n\n#### Clear\n\n```cpp\nvoid ClearAllVariables();\nvoid Clear(char *key_name); //clear a specific variable from memory\n```\n\n\n#### Verify\n\n```cpp\nbool isValidType(char *key_name, char type); //verify if the variable of this name and type exists\nbool isValid(char *key_name); //verify if variable exists\n```\n\n\n\n### Data functions\n\n\n```cpp\nvoid DataClear(); //clear if there is some data in memory. It's cleared after Commit anyway.\nvoid newDataRow(void);  //start a new data row with a timestamp\nbool push_float(char *data_heading, double value);\nbool push_long(char *data_heading, long value);\nbool push_ulong(char *data_heading, unsigned long value);\nbool push_int(char *data_heading, int value);\nbool push_String(char *data_heading, String value);\n\nint CommitData(TCPClientObj *TCPclient);\nint SendJSON(TCPClientObj *TCPclient, String data_json);\n\n//must use with the class prefix as Console.push_int('a', 1)\n```\n\n\n### Time functions\n\n```cpp\n// Real time can be parsed if the runSync() has been called at least once since restart\nunsigned long realtime(); //UTC epoch in seconds\nuint8_t weekday(void); //0=sunday,1=monday, 6=saturday\nuint8_t year(void);\t//021 for year 2021, 121 for year 2121\nuint8_t month(void); //1-12\nuint8_t date(void); //1-31\nuint8_t hour(void); //0-24, UTC (set Timezone from console)\nuint8_t minute(void); ///0-59\n\n//must use with the class prefix as Console.weekday()\n\n```\n\n## Error codes\n```cpp\nERR_FAILED_CONN -1      //Connection failed to the server\nERR_FAILED_CONN_10x -10 //10 times consistent failure. Probably no internet connection.\nERR_NO_NODE_TOKEN -2    //NT isn't identified, call Console.Identify(\u0026TCPclient, mac_address); for a new NT.\nERR_ACK_FAILED -3       //usually due to wrong account security details. UDP Console.log() might still work because that's not encrypted.\nERR_DATA_PARSE -4       //usually due to wrong TTC or JSON syntax, try removing quotations.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftabahi%2Ftabahiconsole","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftabahi%2Ftabahiconsole","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftabahi%2Ftabahiconsole/lists"}