From 90f7af7bdcb4b45255d9bb7fb0df8f4527aab9cd Mon Sep 17 00:00:00 2001 From: Patrick Balsiger Date: Fri, 23 Nov 2018 14:38:39 +0100 Subject: [PATCH] basic irc plugin --- .gitignore | 72 ++++++++++++++++++++++ .gitlab-ci.yml | 14 +++++ .travis.yml | 67 ++++++++++++++++++++ .vscode/extensions.json | 7 +++ .vscode/settings.json | 6 ++ data/config.json | 13 ++++ data/ircConfig.json | 6 ++ lib/readme.txt | 41 +++++++++++++ library.json | 25 ++++++++ platformio.ini | 28 +++++++++ src/IrcConfig.h | 44 ++++++++++++++ src/IrcPlugin.cpp | 118 ++++++++++++++++++++++++++++++++++++ src/IrcPlugin.h | 47 ++++++++++++++ src/examples/basic/config.h | 38 ++++++++++++ src/examples/basic/main.cpp | 45 ++++++++++++++ 15 files changed, 571 insertions(+) create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 .travis.yml create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 data/config.json create mode 100644 data/ircConfig.json create mode 100644 lib/readme.txt create mode 100644 library.json create mode 100644 platformio.ini create mode 100644 src/IrcConfig.h create mode 100644 src/IrcPlugin.cpp create mode 100644 src/IrcPlugin.h create mode 100644 src/examples/basic/config.h create mode 100644 src/examples/basic/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8dcb28b --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + + +# platformio / IDE +.pioenvs +.piolibdeps +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json + +.clang_complete +.gcc-flags.json +.pioenvs +.piolibdeps + data/config.json \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..00f20b5 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,14 @@ +image: python:2.7-stretch + +stages: + - build + +before_script: + - "pip install -U platformio" + +examples: + stage: build + image: python:2.7-stretch + script: + - pio run -t clean + - pio run -e basic \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9443843 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,67 @@ +# Continuous Integration (CI) is the practice, in software +# engineering, of merging all developer working copies with a shared mainline +# several times a day < http://docs.platformio.org/page/ci/index.html > +# +# Documentation: +# +# * Travis CI Embedded Builds with PlatformIO +# < https://docs.travis-ci.com/user/integration/platformio/ > +# +# * PlatformIO integration with Travis CI +# < http://docs.platformio.org/page/ci/travis.html > +# +# * User Guide for `platformio ci` command +# < http://docs.platformio.org/page/userguide/cmd_ci.html > +# +# +# Please choice one of the following templates (proposed below) and uncomment +# it (remove "# " before each line) or use own configuration according to the +# Travis CI documentation (see above). +# + + +# +# Template #1: General project. Test it using existing `platformio.ini`. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio run + + +# +# Template #2: The project is intended to by used as a library with examples +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# env: +# - PLATFORMIO_CI_SRC=path/to/test/file.c +# - PLATFORMIO_CI_SRC=examples/file.ino +# - PLATFORMIO_CI_SRC=path/to/test/directory +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..8281e64 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1888b54 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "terminal.integrated.env.linux": { + "PATH": "/home/master/.platformio/penv/bin:/home/master/.platformio/penv:/usr/local/sbin:/usr/local/bin:/usr/bin", + "PLATFORMIO_CALLER": "vscode" + } +} \ No newline at end of file diff --git a/data/config.json b/data/config.json new file mode 100644 index 0000000..82c9e85 --- /dev/null +++ b/data/config.json @@ -0,0 +1,13 @@ +{ + "stationMode": 1, + "hostname": "sprocket", + "apSSID": "sprocket", + "apPassword": "th3r31sn0Sp00n", + "connectTimeout": 20000, + "stationSSID": "MyAP", + "stationPassword": "th3r31sn0Sp00n", + "meshSSID": "whateverYouLike", + "meshPassword": "somethingSneaky", + "meshPort": 5555, + "channel": 5 +} \ No newline at end of file diff --git a/data/ircConfig.json b/data/ircConfig.json new file mode 100644 index 0000000..4c19e22 --- /dev/null +++ b/data/ircConfig.json @@ -0,0 +1,6 @@ +{ + "ircServer": "chat.freenode.net", + "ircPort": 6665, + "ircNickname": "sprocket", + "ircUser": "sprocket" +} \ No newline at end of file diff --git a/lib/readme.txt b/lib/readme.txt new file mode 100644 index 0000000..131f1bf --- /dev/null +++ b/lib/readme.txt @@ -0,0 +1,41 @@ + +This directory is intended for the project specific (private) libraries. +PlatformIO will compile them to static libraries and link to executable file. + +The source code of each library should be placed in separate directory, like +"lib/private_lib/[here are source files]". + +For example, see how can be organized `Foo` and `Bar` libraries: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) http://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- readme.txt --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +Then in `src/main.c` you should use: + +#include +#include + +// rest H/C/CPP code + +PlatformIO will find your libraries automatically, configure preprocessor's +include paths and build them. + +More information about PlatformIO Library Dependency Finder +- http://docs.platformio.org/page/librarymanager/ldf.html diff --git a/library.json b/library.json new file mode 100644 index 0000000..d9a80a8 --- /dev/null +++ b/library.json @@ -0,0 +1,25 @@ +{ + "name": "sprocket-plugin-mqtt", + "keywords": "sprocket, plugin", + "description": "Plugin for message dispatching a Sprocket and a MQTT queue.", + "authors": + { + "name": "Patrick Balsiger", + "email": "patrick.balsiger@wirelos.net", + "url": "https://gitlab.com/0x1d" + }, + "repository": + { + "type": "git", + "url": "https://gitlab.com/wirelos/sprocket-plugin-mqtt" + }, + "frameworks": "arduino", + "platforms": "espressif8266, esp32", + "examples": "examples/*", + "export": { + "exclude": + [ + "src/examples/" + ] + } +} \ No newline at end of file diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..02b0afc --- /dev/null +++ b/platformio.ini @@ -0,0 +1,28 @@ +[platformio] +env_default = basic + +[common] +framework = arduino +platform = espressif8266 +board = esp12e +upload_speed = 921600 +monitor_baud = 115200 +lib_deps = + Hash + TaskScheduler + SPIFFS + ArduinoJson + ArduinoOTA + https://gitlab.com/wirelos/sprocket-lib.git#develop + https://gitlab.com/wirelos/sprocket-network-wifi.git + +[env:basic] +src_filter = +<*> - + +platform = ${common.platform} +board = ${common.board} +upload_speed = ${common.upload_speed} +monitor_baud = ${common.monitor_baud} +framework = ${common.framework} +lib_deps = ${common.lib_deps} + ESP8266mDNS + ArduinoIRC \ No newline at end of file diff --git a/src/IrcConfig.h b/src/IrcConfig.h new file mode 100644 index 0000000..f246bc8 --- /dev/null +++ b/src/IrcConfig.h @@ -0,0 +1,44 @@ +#ifndef __IRC_CONFIG__ +#define __IRC_CONFIG__ + +#include + +#define JSON_IRC_SERVER "ircServer" +#define JSON_IRC_PORT "ircPort" +#define JSON_IRC_NICKNAME "ircNickname" +#define JSON_IRC_USER "ircUser" + +struct IrcConfig +{ + const char *server; + int port; + const char *nickname; + const char *user; +}; + +struct IrcConfigJson : public IrcConfig, public JsonStruct +{ + void mapJsonObject(JsonObject &root) + { + root[JSON_IRC_SERVER] = server; + root[JSON_IRC_PORT] = port; + root[JSON_IRC_NICKNAME] = nickname; + root[JSON_IRC_USER] = user; + } + void fromJsonObject(JsonObject &json) + { + if (!verifyJsonObject(json)) + { + Serial.println("ERROR: cannot parse JSON object"); + valid = 0; + return; + } + server = getAttr(json, JSON_IRC_SERVER); + port = getIntAttrFromJson(json, JSON_IRC_PORT); + nickname = getAttr(json, JSON_IRC_NICKNAME); + user = getAttr(json, JSON_IRC_USER); + valid = 1; + }; +}; + +#endif \ No newline at end of file diff --git a/src/IrcPlugin.cpp b/src/IrcPlugin.cpp new file mode 100644 index 0000000..04d8bad --- /dev/null +++ b/src/IrcPlugin.cpp @@ -0,0 +1,118 @@ +#include "IrcPlugin.h" + +IrcPlugin::IrcPlugin(IrcConfig cfg) +{ + applyConfig(cfg); +} + +void IrcPlugin::applyConfig(IrcConfig cfg) +{ + server = String(cfg.server); + port = cfg.port; + nick = String(cfg.nickname); + user = String(cfg.user); +} + +void IrcPlugin::applyConfigFromFile(const char *fileName) +{ + IrcConfigJson configFile; + configFile.fromFile(fileName); + if (configFile.valid) + { + PRINT_MSG(Serial, "MQTT", "apply config from file"); + applyConfig(configFile); + } +} + +void IrcPlugin::activate(Scheduler *scheduler) +{ + applyConfigFromFile("/ircConfig.json"); + client = new IRCClient(server.c_str(), port, wifiClient); + client->setCallback(bind(&IrcPlugin::callback, this, _1)); + client->setSentCallback(bind(&IrcPlugin::debugSentCallback, this, _1)); + + String channel = "#illucat"; + + subscribe("irc/connect", bind(&IrcPlugin::connect, this)); + subscribe("irc/join", bind(&IrcPlugin::join, this, _1)); + subscribe("irc/sendMessage", bind(&IrcPlugin::sendMessage, this, channel, _1)); + + //enableConnectTask(scheduler); + enableProcessTask(scheduler); + PRINT_MSG(Serial, "MQTT", "plugin activated"); +} + +void IrcPlugin::enableConnectTask(Scheduler *scheduler) +{ + connectTask.set(TASK_SECOND * 5, TASK_FOREVER, bind(&IrcPlugin::connect, this)); + scheduler->addTask(connectTask); + connectTask.enable(); +} + +void IrcPlugin::enableProcessTask(Scheduler *scheduler) +{ + processTask.set(TASK_MILLISECOND * 5, TASK_FOREVER, bind(&IRCClient::loop, client)); + scheduler->addTask(processTask); + processTask.enable(); +} + +void IrcPlugin::connect() +{ + if (!client->connected()) + { + PRINT_MSG(Serial, "IRC", String("Attempting connection to " + server).c_str()); + if (client->connect(nick.c_str(), user.c_str())) + { + PRINT_MSG(Serial, "IRC", "connected"); + } + else + { + PRINT_MSG(Serial, "IRC", "failed... "); + } + return; + } +} + +void IrcPlugin::join(String channel) +{ + if (client->connected()) + { + + String joinCmd = "JOIN :" + channel + "\r\n"; + wifiClient.print(joinCmd.c_str()); + PRINT_MSG(Serial, "IRC", joinCmd.c_str()); + } +} + +void IrcPlugin::sendMessage(String to, String msg) +{ + client->sendMessage(to, msg); +} + +void IrcPlugin::callback(IRCMessage ircMessage) +{ + + String message("<" + ircMessage.nick + "> " + ircMessage.text); + publish("irc/log", message); + PRINT_MSG(Serial, "IRC", message.c_str()); + + // PRIVMSG ignoring CTCP messages + /* if (ircMessage.command == "PRIVMSG" && ircMessage.text[0] != '\001') + { + String message("<" + ircMessage.nick + "> " + ircMessage.text); + PRINT_MSG(Serial, "IRC", message.c_str()); + + if (ircMessage.nick == REPLY_TO) + { + client->sendMessage(ircMessage.nick, "Hi " + ircMessage.nick + "! I'm your IRC bot."); + } + + return; + } */ + PRINT_MSG(Serial, "IRC", ircMessage.original.c_str()); +} + +void IrcPlugin::debugSentCallback(String data) +{ + PRINT_MSG(Serial, "IRC", data.c_str()); +} \ No newline at end of file diff --git a/src/IrcPlugin.h b/src/IrcPlugin.h new file mode 100644 index 0000000..1712426 --- /dev/null +++ b/src/IrcPlugin.h @@ -0,0 +1,47 @@ +#ifndef __IRC_PLUGIN__ +#define __IRC_PLUGIN__ + +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STD_FUNCTION + +#include +#include +#include +#include "utils/print.h" +#include "IrcConfig.h" + +using namespace std; +using namespace std::placeholders; + +#define REPLY_TO "mentex" // Reply only to this nick + +class IrcPlugin : public Plugin +{ +public: + + IrcPlugin(IrcConfig cfg); + + void activate(Scheduler *scheduler); + +private: + WiFiClient wifiClient; + IRCClient* client; + Task connectTask; + Task processTask; + String server; + int port; + String nick; + String user; + + void applyConfig(IrcConfig cfg); + void applyConfigFromFile(const char *fileName); + void enableConnectTask(Scheduler *scheduler); + void enableProcessTask(Scheduler *scheduler); + virtual void connect(); + virtual void callback(IRCMessage ircMessage); + virtual void debugSentCallback(String data); + void join(String channel); + void sendMessage(String to, String msg); +}; + +#endif \ No newline at end of file diff --git a/src/examples/basic/config.h b/src/examples/basic/config.h new file mode 100644 index 0000000..b78a35b --- /dev/null +++ b/src/examples/basic/config.h @@ -0,0 +1,38 @@ +#ifndef __DEVICE_CONFIG__ +#define __DEVICE_CONFIG__ + +// Scheduler config +#define _TASK_SLEEP_ON_IDLE_RUN +#define _TASK_STD_FUNCTION +#define _TASK_PRIORITY + +// Chip config +#define SPROCKET_TYPE "SPROCKET" +#define SERIAL_BAUD_RATE 115200 +#define STARTUP_DELAY 1000 + +// network config +#define SPROCKET_MODE 1 +#define WIFI_CHANNEL 11 +#define MESH_PORT 5555 +#define AP_SSID "sprocket" +#define AP_PASSWORD "th3r31sn0sp00n" +#define MESH_PREFIX "sprocket-mesh" +#define MESH_PASSWORD "th3r31sn0sp00n" +#define STATION_SSID "MyAP" +#define STATION_PASSWORD "th3r31sn0sp00n" +#define HOSTNAME "sprocket" +#define CONNECT_TIMEOUT 10000 +#define MESH_DEBUG_TYPES ERROR | STARTUP | CONNECTION +//#define MESH_DEBUG_TYPES ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE + +// WebServer +#define WEB_CONTEXT_PATH "/" +#define WEB_DOC_ROOT "/www" +#define WEB_DEFAULT_FILE "index.html" +#define WEB_PORT 80 + +#define MQTT_CONFIG_FILE "/mqttConfig.json" + + +#endif \ No newline at end of file diff --git a/src/examples/basic/main.cpp b/src/examples/basic/main.cpp new file mode 100644 index 0000000..8894bb3 --- /dev/null +++ b/src/examples/basic/main.cpp @@ -0,0 +1,45 @@ +//#include +//#include +#include "config.h" +#include "WiFiNet.h" +#include "Sprocket.h" +#include "IrcPlugin.h" + +#define IRC_SERVER "chat.freenode.net" +#define IRC_PORT 6665 +#define IRC_NICKNAME "illucat" +#define IRC_USER "illucat" + +WiFiNet *network; +Sprocket *sprocket; +IrcPlugin *irc; + +void setup() +{ + + delay(1000); + + sprocket = new Sprocket({STARTUP_DELAY, SERIAL_BAUD_RATE}); + irc = new IrcPlugin({IRC_SERVER, IRC_PORT, IRC_NICKNAME, IRC_USER}); + sprocket->addPlugin(irc); + network = new WiFiNet( + SPROCKET_MODE, + STATION_SSID, + STATION_PASSWORD, + AP_SSID, + AP_PASSWORD, + HOSTNAME, + CONNECT_TIMEOUT); + network->connect(); + + sprocket->activate(); + sprocket->publish("irc/connect", ""); + sprocket->publish("irc/join", "#illucat"); + sprocket->publish("irc/sendMessage", "hoiii"); +} + +void loop() +{ + sprocket->loop(); + yield(); +} \ No newline at end of file