PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

442 lines
11KB

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