diff --git a/espnow/src/main.cpp b/espnow/src/main.cpp index 728011e..b3197c0 100644 --- a/espnow/src/main.cpp +++ b/espnow/src/main.cpp @@ -1,3 +1,8 @@ +// we want to first osc -> esp-now +// then, esp-now based taak +// then, let is save a value in EEPROM (object with memory) +// no broadcast for now. if needed we can achieve that too. + #include #include #include diff --git a/osc/platformio.ini b/osc/platformio.ini index 3fa1016..8efef27 100644 --- a/osc/platformio.ini +++ b/osc/platformio.ini @@ -14,6 +14,7 @@ env_default = teensy36 [common] lib_deps = 721@3.0.2 ; TaskScheduler + 5825 ; Vector ; osc -> (already included in "framework-arduinoteensy") diff --git a/osc/src/main.cpp b/osc/src/main.cpp index 3cc39b7..cc469de 100644 --- a/osc/src/main.cpp +++ b/osc/src/main.cpp @@ -1,5 +1,5 @@ // -// wirelessly connected cloud (Wireless Mesh Networking) +// wirelessly connected cloud (based on ESP-NOW, a kind of LPWAN?) // // @@ -9,7 +9,7 @@ // // 2021 02 15 // -// (part-2) teensy35 : 'client:osc' (osc over slip --> mesh post) +// (part-2) teensy35 : 'client:osc' (osc over slip <--> esp-now 'post') // //arduino @@ -26,9 +26,6 @@ SLIPEncodedUSBSerial SLIPSerial(Serial); //postman's uart #define POSTMAN_SERIAL (Serial3) -// --[post]--> "sampler" -#include "../../post.h" - // void setup() { //osc @@ -39,57 +36,37 @@ void setup() { } // -void midinote(OSCMessage& msg, int offset) { +void route_note(OSCMessage& msg, int offset) { // matches will happen in the order. that the bundle is packed. - static int pitch = 0; - static int velocity = 0; - static int onoff = 0; - static int x1 = 0; - static int x2 = 0; - static int x3 = 0; - static int x4 = 0; - static int ps = 0; + static Note note; // (1) --> /onoff if (msg.fullMatch("/onoff", offset)) { // - pitch = 0; - velocity = 0; - onoff = 0; + note.clear(); // - onoff = msg.getInt(0); - if (onoff != 0) onoff = 1; + note.onoff = msg.getInt(0); + if (note.onoff != 0) note.onoff = 1; } // (2) --> /velocity if (msg.fullMatch("/velocity", offset)) { - velocity = msg.getInt(0); - if (velocity < 0) velocity = 0; - if (velocity > 999) velocity = 999; + note.velocity = msg.getInt(0); } // (3) --> /pitch if (msg.fullMatch("/pitch", offset)) { - pitch = msg.getInt(0); - if (pitch < 0) pitch = 0; - if (pitch > 999) pitch = 999; + note.pitch = msg.getInt(0); } // (4) --> /x if (msg.fullMatch("/x", offset)) { - x1 = msg.getInt(0); - x2 = msg.getInt(1); - x3 = msg.getInt(2); - x4 = msg.getInt(3); - ps = msg.getInt(4); - - char letter[POST_BUFF_LEN] = ""; - snprintf(letter, POST_BUFF_LEN, "[%03d%03d%01dX%05d%05d%05d%05d%02d]", - pitch, - velocity, - onoff, - x1, - x2, - x3, - x4, - ps); - POSTMAN_SERIAL.print(letter); + note.x1 = msg.getInt(0); + note.x2 = msg.getInt(1); + note.x3 = msg.getInt(2); + note.x4 = msg.getInt(3); + note.ps = msg.getInt(4); + // + POSTMAN_SERIAL.write('['); // start byte of 'Note' + POSTMAN_SERIAL.write((uint8_t *) ¬e, sizeof(Note)); + POSTMAN_SERIAL.write(']'); // end byte of 'Note' + // } } @@ -108,7 +85,8 @@ void loop() { } } if(!bundleIN.hasError()) { - bundleIN.route("/note", midinote); + // on '/note' + bundleIN.route("/note", route_note); } } @@ -116,79 +94,58 @@ void loop() { static bool insync = false; if (insync == false) { while (POSTMAN_SERIAL.available() > 0) { - // check the last byte + // search the last byte char last = POSTMAN_SERIAL.read(); // expectable last of the messages if (last == ']' || last == '}') { insync = true; } } - } - if (POSTMAN_SERIAL.available() > POST_LENGTH) { - char cstr[POST_BUFF_LEN] = "................................"; - // fetch all the bytes - POSTMAN_SERIAL.readBytes(cstr, POST_LENGTH); - // protocol checks - char first = cstr[0]; - char last = cstr[POST_LENGTH-1]; - if (first != '[' && first != '{') { - insync = false; - } - if (last != ']' && last != '}') { - insync = false; - } - - //// OK -> parse && compose & send OSC message! - - String msg = String(cstr); - - // hello frame ( '{' + 30 bytes + '}' ) - // : {123456789012345678901234567890} - - // hello frame - // : {123456789012345678901234567890} - // : {IIIA111111222222333333444444__} - // : III - ID_KEY - // .substring(1, 4); - // : 1 - data of 6 letters - // .substring(9, 14); - // : 2 - data of 6 letters - // .substring(14, 19); - // : 3 - data of 6 letters - // .substring(19, 24); - // : 4 - data of 6 letters - // .substring(24, 29); - - // received a hello. - String str_id = msg.substring(1, 4); - int id = str_id.toInt(); - + } else { // - OSCMessage hello("/hello"); - hello.add(id); - - // - String str_aa = msg.substring(4, 5); - - // - if (str_aa == "A") { + if (POSTMAN_SERIAL.available() > 0) { // - String str_h1 = msg.substring(5, 11); - String str_h2 = msg.substring(11, 17); - String str_h3 = msg.substring(17, 23); - String str_h4 = msg.substring(23, 29); - + char type = POSTMAN_SERIAL.peek(); // - hello.add(str_h1.toInt()); - hello.add(str_h2.toInt()); - hello.add(str_h3.toInt()); - hello.add(str_h4.toInt()); + if (type == '{') { + //expecting a Hello message. + if (POSTMAN_SERIAL.available() > sizeof(Hello) + 1) { + Hello hello; + POSTMAN_SERIAL.readBytes((uint8_t *) &hello, sizeof(Hello)); + char last = POSTMAN_SERIAL.read(); + if (last == '}') { + //good. + // + OSCMessage osc("/hello"); + osc.add(hello.id); + osc.add(hello.h1); + osc.add(hello.h2); + osc.add(hello.h3); + osc.add(hello.h4); + // + SLIPSerial.beginPacket(); + osc.send(SLIPSerial); + SLIPSerial.endPacket(); + osc.empty(); + // + } else { + insync = false; //error! + } + } + } else if (type == '[') { + //expecting a Note message. + if (POSTMAN_SERIAL.available() > sizeof(Note) + 1) { + Note note; + POSTMAN_SERIAL.readBytes((uint8_t *) ¬e, sizeof(Note)); + char last = POSTMAN_SERIAL.read(); + if (last == ']') { + //good. + } else { + insync = false; //error! + } + } + // + } } - - // - SLIPSerial.beginPacket(); - hello.send(SLIPSerial); - SLIPSerial.endPacket(); - hello.empty(); } } diff --git a/peers.h b/peers.h new file mode 100644 index 0000000..e69de29 diff --git a/platformio.ini b/platformio.ini index 7fdce6b..18bc5da 100644 --- a/platformio.ini +++ b/platformio.ini @@ -28,7 +28,7 @@ upload_speed = 921600 ; 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 upload_port = /dev/ttyUSB0 - /dev/tty.SLAB_USBtoUART +; /dev/tty.SLAB_USBtoUART lib_deps = SPI Wire diff --git a/post.h b/post.h index 691f996..f89208e 100644 --- a/post.h +++ b/post.h @@ -1,5 +1,85 @@ #pragma once +//obsolete #define I2C_ADDR 3 #define POST_LENGTH 32 #define POST_BUFF_LEN (POST_LENGTH + 1) + +//esp-now +#define MEMBER_COUNT_MAX (20) +struct Address { + String name; + uint8_t mac[6]; + Address() { + mac[0] = 0; + mac[1] = 0; + mac[2] = 0; + mac[3] = 0; + mac[4] = 0; + mac[5] = 0; + name = ""; + }; + Address(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, String n) { + mac[0] = a; + mac[1] = b; + mac[2] = c; + mac[3] = d; + mac[4] = e; + mac[5] = f; + name = n; + } +}; + +//message type Note : '[' + Note + ']' +struct Note { + int32_t pitch; + int32_t velocity; + int32_t onoff; + int32_t x1; + int32_t x2; + int32_t x3; + int32_t x4; + int32_t ps; + // + void clear() { + pitch = 0; + velocity = 0; + onoff = 0; + x1 = 0; + x2 = 0; + x3 = 0; + x4 = 0; + ps = 0; + } + // + String to_string() { + String str = ""; + str += "( pitch=" + String(pitch); + str += ", velocity=" + String(velocity); + str += ", onoff=" + String(onoff); + str += ", x1=" + String(x1); + str += ", x2=" + String(x2); + str += ", x3=" + String(x3); + str += ", x4=" + String(x4); + str += ", ps=" + String(ps); + str += " )"; + return str; + } +}; + +//message type Hello : '{' + Hello + '}' +struct Hello { + int32_t id; + int32_t h1; + int32_t h2; + int32_t h3; + int32_t h4; + // + void clear() { + id = 0; + h1 = 0; + h2 = 0; + h3 = 0; + h4 = 0; + } +}; diff --git a/postman/platformio.ini b/postman/platformio.ini new file mode 100644 index 0000000..212ab50 --- /dev/null +++ b/postman/platformio.ini @@ -0,0 +1,45 @@ +; < NOTE > + +; to enable verbose output add option --> +; $ platformio run --verbose + +; to make this permanent for the proj. --> +; $ platformio settings set force_verbose Yes + +; then confirm the change --> +; $ platformio settings get + + +; // pio v 4.0 'Build options' +; - build_type +; - build_flags +; - src_build_flags +; - build_unflags +; - src_filter +; - targets + + +[platformio] +default_envs = d1_mini_pro + +[env] +framework = arduino +upload_speed = 460800 +upload_port = /dev/ttyUSB0 +lib_deps = + 5825 ; Vector + 721 ; TaskScheduler + +[env:nodemcuv2] +platform = espressif8266 +board = nodemcuv2 +lib_deps = + ${env.lib_deps} + +[env:d1_mini_pro] +platform = espressif8266 +board = d1_mini_pro +upload_speed = 460800 +; 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 +lib_deps = + ${env.lib_deps} diff --git a/postman/src/main.cpp b/postman/src/main.cpp new file mode 100644 index 0000000..8c6012c --- /dev/null +++ b/postman/src/main.cpp @@ -0,0 +1,332 @@ +// +// wirelessly connected cloud (based on ESP-NOW, a kind of LPWAN?) +// + +// +// Conversation about the ROOT @ SEMA warehouses, Seoul +// + +// +// 2021 02 15 +// +// (part-1) esp8266 : 'postman' (the esp-now network nodes) +// +// this module will be an esp-now node in a group. +// like, a bird in a group of birds. +// +// esp-now @ esp8266 DO support broadcast address (FF:FF:FF:FF:FF:FF) +// w/ NONOS-SDK of espressif +// and you can enable that w/ Platformio, applying some special build flags. +// --> https://github.com/esp8266/Arduino/issues/6174#issuecomment-509115454 +// (yet, w/ Arduino, this is not available yet.) +// +// so, at first, we will simply/stably go w/o broadcasting. +// and, if broadcast is really needed we can activate (@Platformio) +// + + +// we want to first osc -> esp-now +// then, esp-now based taak +// then, let is save a value in EEPROM (object with memory) +// no broadcast for now. if needed we can achieve that too. + + + +//===================== +// +// 'HAVE_CLIENT' +// --> i have a client. enable the client task. +// +// 'SERIAL_SWAP' +// --> UART pin swapped. +// you want this, when you want a bi-directional comm. to external client boards (e.g. teensy). +// +// 'DISABLE_AP' +// --> (questioning)... +// +//==================== + +//===================== +// +// (1) standalone +#if 1 +// (2) osc client (the ROOT) +#elif 0 +#define SERIAL_SWAP +#define HAVE_CLIENT +// (3) sampler client +#elif 0 +#define SERIAL_SWAP +#define HAVE_CLIENT +#define DISABLE_AP +// +#endif +// +//==================== + +//======================== +// +#define LED_PERIOD (1111) +#define LED_ONTIME (1) +#define LED_GAPTIME (222) +// +#define WIFI_CHANNEL 5 +// +// 'MONITORING_SERIAL' +// +// --> sometimes, the 'Serial' is in use (for example, 'osc' node) +// then, use 'Serial1' - D4/GPIO2/TDX1 @ nodemcu (this is TX only.) +// +// --> otherwise, MONITORING_SERIAL == Serial. +// +#if defined(SERIAL_SWAP) +#define MONITORING_SERIAL (Serial1) +#else +#define MONITORING_SERIAL (Serial) +#endif +// +//======================= + +//======================== +#if defined(ARDUINO_FEATHER_ESP32) // featheresp32 +#define LED_PIN 13 +#else +#define LED_PIN 2 +#endif +//======================= + +//arduino +#include + +//post +#include "../../post.h" + +//addresses +#include +Vector
members; +Address __members[MEMBER_COUNT_MAX]; //<-- the storage array of 'members' + +//espnow +#include +#include + +// on 'sent' +void onDataSent(uint8_t *mac_addr, uint8_t sendStatus) { + if (sendStatus != 0) MONITORING_SERIAL.println("Delivery failed!"); +} + +// on 'receive' +void onDataReceive(uint8_t * mac, uint8_t *incomingData, uint8_t len) { + + // +#if defined(HAVE_CLIENT) + Serial.write(incomingData, len); // we pass it over to the client. +#endif + + // open => identify => use. + if (incomingData[0] == '[' && incomingData[len - 1] == ']' && len == (sizeof(Note) + 2)) { + Note note; + memcpy((uint8_t *) ¬e, incomingData + 1, sizeof(Note)); + // + MONITORING_SERIAL.println(note.to_string()); + } +} + +//task +#include +Scheduler runner; + +//task #0 : blink led +extern Task blink_task; +void blink() { + // + static int count = 0; + count++; + // + switch (count % 4) { + case 0: + digitalWrite(LED_PIN, LOW); // first ON + blink_task.delay(LED_ONTIME); + break; + case 1: + digitalWrite(LED_PIN, HIGH); // first OFF + blink_task.delay(LED_GAPTIME); + break; + case 2: + digitalWrite(LED_PIN, LOW); // second ON + blink_task.delay(LED_ONTIME); + break; + case 3: + digitalWrite(LED_PIN, HIGH); // second OFF + blink_task.delay(LED_PERIOD - 2* LED_ONTIME - LED_GAPTIME); + break; + } +} +Task blink_task(0, TASK_FOREVER, &blink, &runner, true); // -> ENABLED, at start-up. + +//task #1 : regular post collection +#if defined(HAVE_CLIENT) +void collect_post() { + // + //postman (serial comm.) + static bool insync = false; + if (insync == false) { + while (Serial.available() > 0) { + // search the last byte + char last = Serial.read(); + // expectable last of the messages + if (last == ']' || last == '}') { + insync = true; + } + } + } else { + // + if (Serial.available() > 0) { + // + char type = Serial.peek(); + // + if (type == '[') { + //expecting a Note message. + uint8_t frm_size = sizeof(Note) + 2; + // + if (Serial.available() >= frm_size) { + // + uint8_t frm[frm_size]; + // + Serial.readBytes(frm, frm_size); + char first = frm[0]; + char last = frm[frm_size - 1]; + if (first == '[' && last == ']') { + // + //good. ==> ok, post it. + // + //pseudo-broadcast using addressbook! + // + for (uint32_t i = 0; i < members.size(); i++) { + esp_now_send(members[i].mac, frm, frm_size); + // + MONITORING_SERIAL.write(frm, frm_size); + MONITORING_SERIAL.print(" ==(esp_now_send)==> "); + // + MONITORING_SERIAL.print(members[i].mac[0], HEX); + for (int j = 1; j < 6; j++) { + MONITORING_SERIAL.print(":"); + MONITORING_SERIAL.print(members[i].mac[j], HEX); + } + MONITORING_SERIAL.print(" ==> " + members[i].name); + // + } + // + } else { + insync = false; //error! + } + } + } + } + } +} +Task collect_post_task(1, TASK_FOREVER, &collect_post, &runner, true); // by default, ENABLED +#endif + +// +void setup() { + + //led + pinMode(LED_PIN, OUTPUT); + + //serial + Serial.begin(115200); + delay(100); + + //members + members.setStorage(__members); + + // + members.push_back(Address(0xF4, 0xCF, 0xA2, 0xED, 0xB7, 0x21, "Enchovy")); + members.push_back(Address(0xF4, 0xCF, 0xA2, 0xED, 0xB3, 0xC5, "Schpaarow")); + + //info + Serial.println(); + Serial.println(); + Serial.println("\"hi, i m your postman.\""); + Serial.println("-"); + Serial.println("- * info >>>"); + Serial.println("- mac address: " + WiFi.macAddress()); + Serial.println("- wifi channel: " + String(WIFI_CHANNEL)); + Serial.println("-"); + Serial.println("- * conf >>>"); +#if defined(HAVE_CLIENT) + Serial.println("- ======== 'HAVE_CLIENT' ========"); +#endif +#if defined(SERIAL_SWAP) + Serial.println("- ======== 'SERIAL_SWAP' ========"); +#endif +#if defined(DISABLE_AP) + Serial.println("- ======== 'DISABLE_AP' ========"); +#endif + Serial.println("-"); + Serial.println("- * addresses >>>"); + for (uint32_t i = 0; i < members.size(); i++) { + Serial.print("- #" + String(i) + " : "); + Serial.print(members[i].mac[0], HEX); + for (int j = 1; j < 6; j++) { + Serial.print(":"); + Serial.print(members[i].mac[j], HEX); + } + Serial.print(" ==> " + members[i].name); + Serial.println(); + } + Serial.println("-"); + Serial.println("\".-.-.-. :)\""); + Serial.println(); + + //wifi + WiFiMode_t node_type = WIFI_AP_STA; +#if defined(DISABLE_AP) + system_phy_set_max_tpw(0); + node_type = WIFI_STA; +#endif + WiFi.mode(node_type); + + //esp-now + if (esp_now_init() != 0) { + Serial.println("Error initializing ESP-NOW"); + return; + } + esp_now_set_self_role(ESP_NOW_ROLE_COMBO); + esp_now_register_send_cb(onDataSent); + esp_now_register_recv_cb(onDataReceive); + for (uint32_t i = 0; i < members.size(); i++) { + esp_now_add_peer(members[i].mac, ESP_NOW_ROLE_COMBO, 1, NULL, 0); // <-- '1' : "Channel does not affect any function" ... *.-a + // + // int esp_now_add_peer(u8 *mac_addr, u8 role, u8 channel, u8 *key, u8 key_len) + // - https://www.espressif.com/sites/default/files/documentation/2c-esp8266_non_os_sdk_api_reference_en.pdf + // + // "Channel does not affect any function, but only stores the channel information + // for the application layer. The value is defined by the application layer. For + // example, 0 means that the channel is not defined; 1 ~ 14 mean valid + // channels; all the rest values can be assigned functions that are specified + // by the application layer." + // - https://www.espressif.com/sites/default/files/documentation/esp-now_user_guide_en.pdf + } + +#if defined(SERIAL_SWAP) + Serial.println("- ======== 'SERIAL_SWAP' ========"); + // a proper say goodbye. + Serial.println("\"bye, i will do 'swap' in 1 second. find me on alternative pins!\""); + Serial.println("\" hint: osc wiring ==> esp8266(serial.swap) <-> teensy(serial3)\""); + Serial.println("-"); + Serial.println("\".-.-.-. :)\""); + delay(1000); // flush out unsent serial messages. + + // moving... + Serial.swap(); // use RXD2/TXD2 pins, afterwards. + delay(100); // wait re-initialization of the 'Serial' +#endif +} + +void loop() { + // + runner.execute(); + // +}