// UDP OSCuino over WIFI w/ ESP32 (or ESP8266? - NOT TESTED) // system, analog and digital pin control and monitoring for Arduino // Yotam Mann and Adrian Freed // #ifdef ESP8266 #include #else #include #endif #include // #include #include #define BOARD_HAS_ANALOG_PULLUP #define BOARD_HAS_CAPACITANCE_SENSING #include char ssid[] = "TEEPOT"; // your network SSID (name) char pass[] = "3333388888"; // your network password // A UDP instance to let us send and receive packets over UDP WiFiUDP Udp; const IPAddress outIp(192,168,43,13); // remote IP (not needed for receive) const unsigned int outPort = 9999; // remote port (not needed for receive) const unsigned int localPort = 8888; // local port to listen for UDP packets (here's where we send the packets) //outgoing messages OSCBundle bundleOUT; //converts the pin to an osc address char * numToOSCAddress (int pin) { static char s[10]; int i = 9; s[i--]= '\0'; do { s[i] = "0123456789"[pin % 10]; --i; pin /= 10; } while (pin && i); s[i] = '/'; return &s[i]; } /** * ROUTES * * these are where the routing functions go * */ /** * DIGITAL * * called when address matched "/d" * expected format: * /d/(pin) * /u = digitalRead with pullup * (no value) = digitalRead without pullup * (value) = digital write on that pin * */ void routeDigital (OSCMessage &msg, int addrOffset) { //match input or output for(byte pin = 0; pin < NUM_DIGITAL_PINS; pin++){ //match against the pin number strings int pinMatched = msg.match(numToOSCAddress(pin), addrOffset); if(pinMatched){ //if it has an int, then it's a digital write if (msg.isInt(0)) { pinMode(pin, OUTPUT); digitalWrite(pin, (msg.getInt(0) > 0) ? HIGH : LOW); } //if it has an float, then it's an analog write else if (msg.isFloat(0)) { //TODO: only for ESP8266 - ESP32 doesn't support analogWrite in this way. #ifdef ESP8266 analogWrite(pin, (int)(msg.getFloat(0)*255.0f)); #endif } //otherwise it's an digital read //with a pullup? else if (msg.fullMatch("/u", pinMatched + addrOffset)) { //set the pullup pinMode(pin, INPUT_PULLUP); //setup the output address which should be /d/(pin)/u char outputAddress[9]; strcpy(outputAddress, "/d"); strcat(outputAddress, numToOSCAddress(pin)); strcat(outputAddress, "/u"); //do the digital read and send the results bundleOUT.add(outputAddress).add(digitalRead(pin)); } //else without a pullup else { //set the pinmode pinMode(pin, INPUT); //setup the output address which should be /d/(pin) char outputAddress[6]; strcpy(outputAddress, "/d"); strcat(outputAddress, numToOSCAddress(pin)); //do the digital read and send the results bundleOUT.add(outputAddress).add(digitalRead(pin)); } } } } /** * ANALOG * * called when the address matches "/a" * * format: * /a/(pin) * /u = analogRead with pullup * (no value) = analogRead without pullup * (digital value) = digital write on that pin * (float value) = analogWrite on that pin * **/ void routeAnalog (OSCMessage &msg, int addrOffset) { //iterate through all the analog pins for (byte pin = 0; pin < NUM_ANALOG_INPUTS; pin++) { //match against the pin number strings int pinMatched = msg.match(numToOSCAddress(pin), addrOffset); if (pinMatched) { //if it has an int, then it's a digital write if (msg.isInt(0)) { pinMode(analogInputToDigitalPin(pin), OUTPUT); digitalWrite(analogInputToDigitalPin(pin), (msg.getInt(0) > 0)? HIGH: LOW); } //if it has an float, then it's an analog write else if(msg.isFloat(0)) { //TODO: only for ESP8266 - ESP32 doesn't support analogWrite in this way. #ifdef ESP8266 analogWrite(pin, (int)(msg.getFloat(0)*255.0f)); #endif } #ifdef BOARD_HAS_ANALOG_PULLUP //with a pullup? else if (msg.fullMatch("/u", pinMatched + addrOffset)){ //set the pullup pinMode(analogInputToDigitalPin(pin), INPUT_PULLUP); //setup the output address which should be /a/(pin)/u char outputAddress[9]; strcpy(outputAddress, "/a"); strcat(outputAddress, numToOSCAddress(pin)); strcat(outputAddress,"/u"); // strcat(outputAddress,"/u"); //do the analog read and send the results bundleOUT.add(outputAddress).add((int32_t)analogRead(pin)); } #endif //else without a pullup else { //set the pinmode pinMode(analogInputToDigitalPin(pin), INPUT); //setup the output address which should be /a/(pin) char outputAddress[6]; strcpy(outputAddress, "/a"); strcat(outputAddress, numToOSCAddress(pin)); //do the analog read and send the results bundleOUT.add(outputAddress).add((int32_t)analogRead(pin)); } } } } #ifdef BOARD_HAS_TONE /** * TONE * * square wave output "/tone" * * format: * /tone/pin * * (digital value) (float value) = freqency in Hz * (no value) disable tone * **/ void routeTone (OSCMessage &msg, int addrOffset) { //iterate through all the analog pins for (byte pin = 0; pin < NUM_DIGITAL_PINS; pin++) { //match against the pin number strings int pinMatched = msg.match(numToOSCAddress(pin), addrOffset); if (pinMatched) { unsigned int frequency = 0; //if it has an int, then it's an integers frequency in Hz if (msg.isInt(0)) { frequency = msg.getInt(0); } //otherwise it's a floating point frequency in Hz else if(msg.isFloat(0)) { frequency = msg.getFloat(0); } else { noTone(pin); } if (frequency > 0) { if(msg.isInt(1)) { tone(pin, frequency, msg.getInt(1)); } else { tone(pin, frequency); } } } } } #endif #ifdef BOARD_HAS_CAPACITANCE_SENSING #define NTPINS 2 const int cpins[NTPINS] = { 4, 15 }; void routeTouch (OSCMessage &msg, int addrOffset) { for (int i = 0; i < NTPINS; ++i) { const char *name = numToOSCAddress(cpins[i]); int pinMatched = msg.match(name, addrOffset); if (pinMatched) { char outputAddress[9]; strcpy(outputAddress, "/c"); strcat(outputAddress, name); bundleOUT.add(outputAddress).add(touchRead(cpins[i])); } } } #endif #ifdef BOARD_HAS_DIE_POWER_SUPPLY_MEASUREMENT #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) float getSupplyVoltage() { int val = analogRead(39); return val>0? (1.20f*1023/val):0.0f; } #else // power supply measurement on some Arduinos float getSupplyVoltage(){ // powersupply int result; // Read 1.1V reference against AVcc #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) ADMUX = _BV(MUX5) | _BV(MUX0); #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) ADMUX = _BV(MUX3) | _BV(MUX2); #elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) || defined(__AVR_ATmega1280__) ADMUX = 0x40| _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) ; ADCSRB = 0; #else ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); #endif delayMicroseconds(300); // wait for Vref to settle ADCSRA |= _BV(ADSC); // Convert while (bit_is_set(ADCSRA,ADSC)); result = ADCL; result |= ADCH<<8; float supplyvoltage = 1.1264f *1023 / result; return supplyvoltage; } #endif #endif #ifdef BOARD_HAS_DIE_TEMPERATURE_SENSOR #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) float getTemperature() { analogReference(INTERNAL); delay(1); int val = analogRead(38); // seems to be flakey analogReference(DEFAULT); return val; //need to compute something here to get to degrees C } #else // temperature float getTemperature() { int result; #if defined(__AVR_ATmega32U4__) ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0); ADCSRB = _BV(MUX5); #else ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3); #endif delayMicroseconds(200); // wait for Vref to settle ADCSRA |= _BV(ADSC); // Convert while (bit_is_set(ADCSRA,ADSC)); result = ADCL; result |= ADCH<<8; analogReference(DEFAULT); return result/1023.0f; } #endif #endif /** * SYSTEM MESSAGES * * expected format: * /s * /m = microseconds * /d = number of digital pins * /a = number of analog pins * /l integer = set the led * /t = temperature * /s = power supply voltage */ // void routeSystem (OSCMessage &msg, int addrOffset) { #ifdef BOARD_HAS_DIE_TEMPERATURE_SENSOR if (msg.fullMatch("/t", addrOffset)){ bundleOUT.add("/s/t").add(getTemperature()); } #endif #ifdef BOARD_HAS_DIE_POWER_SUPPLY_MEASUREMENT if (msg.fullMatch("/s", addrOffset)){ bundleOUT.add("/s/s").add(getSupplyVoltage()); } #endif if (msg.fullMatch("/m", addrOffset)){ bundleOUT.add("/s/m").add((int32_t)micros()); } if (msg.fullMatch("/d", addrOffset)){ bundleOUT.add("/s/d").add(NUM_DIGITAL_PINS); } if (msg.fullMatch("/a", addrOffset)){ bundleOUT.add("/s/a").add(NUM_ANALOG_INPUTS); } if (msg.fullMatch("/l", addrOffset)){ if (msg.isInt(0)) { int i = msg.getInt(0); pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, (i > 0) ? HIGH: LOW); bundleOUT.add("/s/l").add(i); } } } /** * MAIN METHODS * * setup and loop, bundle receiving/sending, initial routing */ void setup() { // pinMode(LED_BUILTIN, OUTPUT); // Serial.begin(115200); // Connect to WiFi network Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); Serial.println("Starting UDP"); Udp.begin(localPort); Serial.print("Local port: "); #ifdef ESP32 Serial.println(localPort); #else Serial.println(Udp.localPort()); #endif #ifdef BOARD_HAS_ANALOG_PULLUP Serial.println("=== BOARD_HAS_ANALOG_PULLUP ==="); #endif #ifdef BOARD_HAS_TONE Serial.println("=== BOARD_HAS_TONE ==="); #endif #ifdef BOARD_HAS_CAPACITANCE_SENSING Serial.println("=== BOARD_HAS_CAPACITANCE_SENSING ==="); #endif #ifdef BOARD_HAS_DIE_POWER_SUPPLY_MEASUREMENT Serial.println("=== BOARD_HAS_DIE_POWER_SUPPLY_MEASUREMENT ==="); #endif #ifdef BOARD_HAS_DIE_TEMPERATURE_SENSOR Serial.println("=== BOARD_HAS_DIE_TEMPERATURE_SENSOR ==="); #endif } //reads and routes the incoming messages void loop(){ OSCBundle bundleIN; int size; if( (size = Udp.parsePacket()) > 0) { while(size--) { bundleIN.fill(Udp.read()); } if(!bundleIN.hasError()) { bundleIN.route("/s", routeSystem); bundleIN.route("/a", routeAnalog); bundleIN.route("/d", routeDigital); #ifdef BOARD_HAS_TONE bundleIN.route("/tone", routeTone); #endif #ifdef BOARD_HAS_CAPACITANCE_SENSING bundleIN.route("/c", routeTouch); #endif } // send the response bundle back to where the request came from Udp.beginPacket(Udp.remoteIP(),outPort); bundleOUT.send(Udp); Udp.endPacket(); // empty the bundle ready to use for new messages bundleOUT.empty(); } }