UDPOscuino.ino 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. // UDP OSCuino
  2. // system, analog and digital pin control and monitoring for Arduino
  3. // Yotam Mann and Adrian Freed
  4. #include <Ethernet.h>
  5. #include <EthernetUdp.h>
  6. #include <SPI.h>
  7. #include <OSCBundle.h>
  8. #include <OSCBoards.h>
  9. EthernetUDP Udp;
  10. //the Arduino's IP
  11. IPAddress ip(128, 32, 122, 252);
  12. //port numbers
  13. const unsigned int inPort = 8888;
  14. const unsigned int outPort = 9999;
  15. //everything on the network needs a unique MAC
  16. #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
  17. // Teensy 3 has MAC burned in
  18. static byte mac[6];
  19. void read(uint8_t word, uint8_t *mac, uint8_t offset) {
  20. FTFL_FCCOB0 = 0x41; // Selects the READONCE command
  21. FTFL_FCCOB1 = word; // read the given word of read once area
  22. // launch command and wait until complete
  23. FTFL_FSTAT = FTFL_FSTAT_CCIF;
  24. while(!(FTFL_FSTAT & FTFL_FSTAT_CCIF));
  25. *(mac+offset) = FTFL_FCCOB5; // collect only the top three bytes,
  26. *(mac+offset+1) = FTFL_FCCOB6; // in the right orientation (big endian).
  27. *(mac+offset+2) = FTFL_FCCOB7; // Skip FTFL_FCCOB4 as it's always 0.
  28. }
  29. void read_mac() {
  30. read(0xe,mac,0);
  31. read(0xf,mac,3);
  32. }
  33. #else
  34. void read_mac() {}
  35. byte mac[] = {
  36. 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // you can find this written on the board of some Arduino Ethernets or shields
  37. #endif
  38. //outgoing messages
  39. OSCBundle bundleOUT;
  40. //converts the pin to an osc address
  41. char * numToOSCAddress( int pin){
  42. static char s[10];
  43. int i = 9;
  44. s[i--]= '\0';
  45. do
  46. {
  47. s[i] = "0123456789"[pin % 10];
  48. --i;
  49. pin /= 10;
  50. }
  51. while(pin && i);
  52. s[i] = '/';
  53. return &s[i];
  54. }
  55. /**
  56. * ROUTES
  57. *
  58. * these are where the routing functions go
  59. *
  60. */
  61. /**
  62. * DIGITAL
  63. *
  64. * called when address matched "/d"
  65. * expected format:
  66. * /d/(pin)
  67. * /u = digitalRead with pullup
  68. * (no value) = digitalRead without pullup
  69. * (value) = digital write on that pin
  70. *
  71. */
  72. void routeDigital(OSCMessage &msg, int addrOffset ){
  73. //match input or output
  74. for(byte pin = 0; pin < NUM_DIGITAL_PINS; pin++){
  75. //match against the pin number strings
  76. int pinMatched = msg.match(numToOSCAddress(pin), addrOffset);
  77. if(pinMatched){
  78. //if it has an int, then it's a digital write
  79. if (msg.isInt(0)){
  80. pinMode(pin, OUTPUT);
  81. digitalWrite(pin, (msg.getInt(0)>0) ? HIGH:LOW);
  82. } //otherwise it's an analog read
  83. else if(msg.isFloat(0)){
  84. analogWrite(pin, (int)(msg.getFloat(0)*255.0f));
  85. }
  86. //otherwise it's an digital read
  87. //with a pullup?
  88. else if (msg.fullMatch("/u", pinMatched+addrOffset)){
  89. //set the pullup
  90. pinMode(pin, INPUT_PULLUP);
  91. //setup the output address which should be /d/(pin)/u
  92. char outputAddress[9];
  93. strcpy(outputAddress, "/d");
  94. strcat(outputAddress, numToOSCAddress(pin));
  95. strcat(outputAddress,"/u");
  96. //do the digital read and send the results
  97. bundleOUT.add(outputAddress).add(digitalRead(pin));
  98. } //else without a pullup
  99. else {
  100. //set the pinmode
  101. pinMode(pin, INPUT);
  102. //setup the output address which should be /d/(pin)
  103. char outputAddress[6];
  104. strcpy(outputAddress, "/d");
  105. strcat(outputAddress, numToOSCAddress(pin));
  106. //do the digital read and send the results
  107. bundleOUT.add(outputAddress).add(digitalRead(pin));
  108. }
  109. }
  110. }
  111. }
  112. /**
  113. * ANALOG
  114. *
  115. * called when the address matches "/a"
  116. *
  117. * format:
  118. * /a/(pin)
  119. * /u = analogRead with pullup
  120. * (no value) = analogRead without pullup
  121. * (digital value) = digital write on that pin
  122. * (float value) = analogWrite on that pin
  123. *
  124. **/
  125. void routeAnalog(OSCMessage &msg, int addrOffset ){
  126. //iterate through all the analog pins
  127. for(byte pin = 0; pin < NUM_ANALOG_INPUTS; pin++){
  128. //match against the pin number strings
  129. int pinMatched = msg.match(numToOSCAddress(pin), addrOffset);
  130. if(pinMatched){
  131. //if it has an int, then it's a digital write
  132. if (msg.isInt(0)){
  133. pinMode(analogInputToDigitalPin(pin), OUTPUT);
  134. digitalWrite(analogInputToDigitalPin(pin), (msg.getInt(0) > 0)? HIGH: LOW);
  135. } //otherwise it's an analog read
  136. else if(msg.isFloat(0)){
  137. analogWrite(pin, (int)(msg.getFloat(0)*255.0f));
  138. }
  139. #ifdef BOARD_HAS_ANALOG_PULLUP
  140. //with a pullup?
  141. else if (msg.fullMatch("/u", pinMatched+addrOffset)){
  142. //set the pullup
  143. pinMode(analogInputToDigitalPin(pin), INPUT_PULLUP);
  144. //setup the output address which should be /a/(pin)/u
  145. char outputAddress[9];
  146. strcpy(outputAddress, "/a");
  147. strcat(outputAddress, numToOSCAddress(pin));
  148. strcat(outputAddress,"/u");
  149. strcat(outputAddress,"/u");
  150. //do the analog read and send the results
  151. bundleOUT.add(outputAddress).add((int32_t)analogRead(pin));
  152. } //else without a pullup
  153. #endif
  154. else {
  155. //set the pinmode
  156. pinMode(analogInputToDigitalPin(pin), INPUT);
  157. //setup the output address which should be /a/(pin)
  158. char outputAddress[6];
  159. strcpy(outputAddress, "/a");
  160. strcat(outputAddress, numToOSCAddress(pin));
  161. //do the analog read and send the results
  162. bundleOUT.add(outputAddress).add((int32_t)analogRead(pin));
  163. }
  164. }
  165. }
  166. }
  167. #ifdef BOARD_HAS_TONE
  168. /**
  169. * TONE
  170. *
  171. * square wave output "/tone"
  172. *
  173. * format:
  174. * /tone/pin
  175. *
  176. * (digital value) (float value) = freqency in Hz
  177. * (no value) disable tone
  178. *
  179. **/
  180. void routeTone(OSCMessage &msg, int addrOffset ){
  181. //iterate through all the analog pins
  182. for(byte pin = 0; pin < NUM_DIGITAL_PINS; pin++){
  183. //match against the pin number strings
  184. int pinMatched = msg.match(numToOSCAddress(pin), addrOffset);
  185. if(pinMatched){
  186. unsigned int frequency = 0;
  187. //if it has an int, then it's an integers frequency in Hz
  188. if (msg.isInt(0)){
  189. frequency = msg.getInt(0);
  190. } //otherwise it's a floating point frequency in Hz
  191. else if(msg.isFloat(0)){
  192. frequency = msg.getFloat(0);
  193. }
  194. else
  195. noTone(pin);
  196. if(frequency>0)
  197. {
  198. if(msg.isInt(1))
  199. tone(pin, frequency, msg.getInt(1));
  200. else
  201. tone(pin, frequency);
  202. }
  203. }
  204. }
  205. }
  206. #endif
  207. #ifdef BOARD_HAS_CAPACITANCE_SENSING
  208. #define NTPINS 12
  209. const int cpins[NTPINS] = {22,23,19,18,17,16,15,0,1,25,32, 33 };
  210. void routeTouch(OSCMessage &msg, int addrOffset )
  211. {
  212. for(int i=0;i<NTPINS;++i)
  213. {
  214. const char *name = numToOSCAddress(cpins[i]);
  215. int pinMatched = msg.match(name, addrOffset);
  216. if(pinMatched)
  217. {
  218. char outputAddress[9];
  219. strcpy(outputAddress, "/c");
  220. strcat(outputAddress, name);
  221. bundleOUT.add(outputAddress).add(touchRead(cpins[i]));
  222. }
  223. }
  224. }
  225. #endif
  226. #ifdef BOARD_HAS_DIE_POWER_SUPPLY_MEASUREMENT
  227. #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
  228. float getSupplyVoltage()
  229. {
  230. int val = analogRead(39);
  231. return val>0? (1.20f*1023/val):0.0f;
  232. }
  233. #else
  234. // power supply measurement on some Arduinos
  235. float getSupplyVoltage(){
  236. // powersupply
  237. int result;
  238. // Read 1.1V reference against AVcc
  239. #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  240. ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  241. #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  242. ADMUX = _BV(MUX5) | _BV(MUX0);
  243. #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  244. ADMUX = _BV(MUX3) | _BV(MUX2);
  245. #elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) || defined(__AVR_ATmega1280__)
  246. ADMUX = 0x40| _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) ;
  247. ADCSRB = 0;
  248. #else
  249. ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  250. #endif
  251. delayMicroseconds(300); // wait for Vref to settle
  252. ADCSRA |= _BV(ADSC); // Convert
  253. while (bit_is_set(ADCSRA,ADSC));
  254. result = ADCL;
  255. result |= ADCH<<8;
  256. float supplyvoltage = 1.1264f *1023 / result;
  257. return supplyvoltage;
  258. }
  259. #endif
  260. #endif
  261. #ifdef BOARD_HAS_DIE_TEMPERATURE_SENSOR
  262. #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
  263. float getTemperature()
  264. {
  265. analogReference(INTERNAL);
  266. delay(1);
  267. int val = analogRead(38); // seems to be flakey
  268. analogReference(DEFAULT);
  269. return val; //need to compute something here to get to degrees C
  270. }
  271. #else
  272. // temperature
  273. float getTemperature(){
  274. int result;
  275. #if defined(__AVR_ATmega32U4__)
  276. ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0);
  277. ADCSRB = _BV(MUX5);
  278. #else
  279. ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3);
  280. #endif
  281. delayMicroseconds(200); // wait for Vref to settle
  282. ADCSRA |= _BV(ADSC); // Convert
  283. while (bit_is_set(ADCSRA,ADSC));
  284. result = ADCL;
  285. result |= ADCH<<8;
  286. analogReference(DEFAULT);
  287. return result/1023.0f;
  288. }
  289. #endif
  290. #endif
  291. /**
  292. * SYSTEM MESSAGES
  293. *
  294. * expected format:
  295. * /s
  296. * /m = microseconds
  297. * /d = number of digital pins
  298. * /a = number of analog pins
  299. * /l integer = set the led
  300. * /t = temperature
  301. * /s = power supply voltage
  302. */
  303. //
  304. void routeSystem(OSCMessage &msg, int addrOffset ){
  305. #ifdef BOARD_HAS_DIE_TEMPERATURE_SENSOR
  306. if (msg.fullMatch("/t", addrOffset)){
  307. bundleOUT.add("/s/t").add(getTemperature());
  308. }
  309. #endif
  310. #ifdef BOARD_HAS_DIE_POWER_SUPPLY_MEASUREMENT
  311. if (msg.fullMatch("/s", addrOffset)){
  312. bundleOUT.add("/s/s").add(getSupplyVoltage());
  313. }
  314. #endif
  315. if (msg.fullMatch("/m", addrOffset)){
  316. bundleOUT.add("/s/m").add((int32_t)micros());
  317. }
  318. if (msg.fullMatch("/d", addrOffset)){
  319. bundleOUT.add("/s/d").add(NUM_DIGITAL_PINS);
  320. }
  321. if (msg.fullMatch("/a", addrOffset)){
  322. bundleOUT.add("/s/a").add(NUM_ANALOG_INPUTS);
  323. }
  324. // this is disabled because many ethernet boards use the
  325. // LED pin for ethernet pin 13
  326. #if LED_BUILTIN!=13
  327. if (msg.fullMatch("/l", addrOffset)){
  328. if (msg.isInt(0)){
  329. pinMode(LED_BUILTIN, OUTPUT);
  330. int i = msg.getInt(0);
  331. pinMode(LED_BUILTIN, OUTPUT);
  332. digitalWrite(LED_BUILTIN, (i > 0)? HIGH: LOW);
  333. bundleOUT.add("/s/l").add(i);
  334. }
  335. }
  336. #endif
  337. }
  338. /**
  339. * MAIN METHODS
  340. *
  341. * setup and loop, bundle receiving/sending, initial routing
  342. */
  343. void setup() {
  344. //setup ethernet port
  345. read_mac();
  346. Ethernet.begin(mac,ip);
  347. Udp.begin(inPort);
  348. }
  349. //reads and routes the incoming messages
  350. void loop(){
  351. OSCBundle bundleIN;
  352. int size;
  353. if( (size = Udp.parsePacket())>0)
  354. {
  355. // unsigned int outPort = Udp.remotePort();
  356. while(size--)
  357. bundleIN.fill(Udp.read());
  358. if(!bundleIN.hasError())
  359. {
  360. bundleIN.route("/s", routeSystem);
  361. bundleIN.route("/a", routeAnalog);
  362. bundleIN.route("/d", routeDigital);
  363. #ifdef BOARD_HAS_TONE
  364. bundleIN.route("/tone", routeTone);
  365. #endif
  366. #ifdef TOUCHSUPPORT
  367. bundleIN.route("/c", routeTouch);
  368. #endif
  369. }
  370. // send the response bundle back to where the request came from
  371. Udp.beginPacket(Udp.remoteIP(),outPort);
  372. bundleOUT.send(Udp);
  373. Udp.endPacket();
  374. bundleOUT.empty(); // empty the bundle ready to use for new messages
  375. }
  376. }