Argentina
Offline
Newbie
Karma: 0
Posts: 17
|
 |
« on: May 18, 2011, 12:43:31 am » |
Hi! I'm working in a application to communicate with my Arduino from Python (using USB interface). It's working on the Arduino UNO. Now I want to make it work on a Mega, and I'm facing a problem: the Mega has many more things that the Uno. If a Python program is written for a Mega, but connected to an Uno, it should report an error!. Or work until the Python program try to use something that the connected Arduino doesn't have (I mean, let it work if the program try to use digital pin 1 to 13, but raise an Exception if try to use digital pin 20). One possible solution: is there any way to check if the hardware has a given port? I would like to do something like: if(digitalPortExists(99)) { // OK. It's safe to use digital port 99 } else { // Send ERROR message to PC. }
Another way would be to send, from the Arduino, what kind of Arduino the program is running on. With this information, I would be able to do the check in Python. And I think this would be better, since knowing the Arduino model, I would be able to check digital and analog pins, what pins have support for PWM, how many interrupts, etc. Something like: char* model = getArduinoMode(); Serial.write(model);
The project I'm working on is at https://github.com/hgdeoro/py-arduino-proxy. The project is a "low level" communication library, but I've crated a simple UI to better illustrate the possibilities: . Thanks in advance! Horacio
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #1 on: May 18, 2011, 02:26:39 am » |
When python is talking to your Arduino you have defined an protocol between these two. You should add a getType request in the protocol and your application on the Arduino can return its type as follows: #include "avr_cpunames.h"
char * getType() { return _AVR_CPU_NAME_; } void setup() { Serial.begin(115200); Serial.println(getType()); } void loop() {}
check - C:\Program Files (x86)\arduino-0022\libraries\ArduinoTestSuite -
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Sr. Member
Karma: 0
Posts: 360
Arduino rocks
|
 |
« Reply #2 on: May 18, 2011, 04:16:47 am » |
That won't necessarily work - the DIP package ATMega328P does not include some of the pins that the SMD package does, so the Arduino Nano has a few extra pins than the Arduino UNO or Duemilanove, while returning the same value for _AVR_CPU_NAME_. Of course, you could just sacrifice these pins and live with it =).
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #3 on: May 18, 2011, 06:05:31 am » |
@Aeternalus Probably your right, an alternative is to define a string yourself, drawback is that you might need to change it if your code is uploaded to a slightly different platform. #define _TYPE_ "ATMega328P MHZ16 ANA6 DIG13 PWM4 HWS1 I2C1 SPI1"
char * getType() { return _TYPE_; } void setup() { Serial.begin(115200); Serial.println(getType()); } void loop() {}
Or even more work struct ArduinoType { char name[20]; uint8_t analogPins; uint8_t digitalPins; uint8_t pwmPins; uint8_t voltage; // whole part only uint16_t RAM; // KB uint16_t EEPROM; // KB uint8_t SerialPorts; uint8_t clockSpeed; // .. } myArduino = { _AVR_CPU_NAME_, 6, 13, 4, 5, ... }
|
|
|
|
|
Logged
|
|
|
|
|
Argentina
Offline
Newbie
Karma: 0
Posts: 17
|
 |
« Reply #4 on: May 18, 2011, 08:03:47 am » |
Thank you for your replies! I like the _AVR_CPU_NAME_ option (less work!), but I'm looking for a way to avoid sacrifice any port. I'll test the struct option.
Thanks again! Horacio
|
|
|
|
|
Logged
|
|
|
|
|
Argentina
Offline
Newbie
Karma: 0
Posts: 17
|
 |
« Reply #5 on: May 18, 2011, 11:02:23 am » |
For digital pins, I've found how to validate if a given pin is valid (pinMode() function in arduino-0022/hardware/arduino/cores/arduino/wiring_digital.c): uint8_t port = digitalPinToPort(pin); if (port == NOT_A_PIN) return;
digitalPinToPort() and NOT_A_PIN are defined in pins_arduino.c, I think I should be safe to use it.
|
|
|
|
|
Logged
|
|
|
|
|
Argentina
Offline
Newbie
Karma: 0
Posts: 17
|
 |
« Reply #6 on: May 18, 2011, 11:59:30 am » |
I've tested digitalPinToPort() and it isn't working... int pin = atoi(received_parameters[1]); uint8_t port = digitalPinToPort(pin); if (port == NOT_A_PIN) { send_invalid_parameter_response(1); return; }
I've sent "99" as pin value, and no error was reported :-( ./bin/ipython_session.py /dev/ttyACM0
Launching IPython shell...
Available variables: - proxy: the ArduinoProxy instance. - options, args: parsed argument options.
To import ArduinoProxy class: >>> from arduino_proxy import ArduinoProxy
Enter 'quit()' to exit.
In [1]: proxy.ping() Out[1]: 'PING_OK'
In [2]: proxy.digitalRead(12) Out[2]: 1
In [3]: proxy.digitalRead(99) Out[3]: 1
|
|
|
|
|
Logged
|
|
|
|
|
Argentina
Offline
Newbie
Karma: 0
Posts: 17
|
 |
« Reply #7 on: May 18, 2011, 12:16:09 pm » |
Returning _AVR_CPU_NAME_ to Python was very easy to implement. Will use that at least for doing some controls. Thanks again! ./bin/ipython_session.py /dev/ttyACM0 (...) Enter 'quit()' to exit.
In [1]: proxy.getAvrCpuType() Out[1]: 'ATmega328P'
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #8 on: May 18, 2011, 12:49:35 pm » |
digitalPinToPort(pin) is a macro reading from array/memory, any number greater than the arraysize is meaningless as it reads from somewhere.
|
|
|
|
|
Logged
|
|
|
|
|
Argentina
Offline
Newbie
Karma: 0
Posts: 17
|
 |
« Reply #9 on: May 18, 2011, 02:20:42 pm » |
Ahhh... thank you! To fix that, I've tried to use sizeof() with the array digital_pin_to_port_PGM (the macro uses this array), to verify if the pin points to an array element, but haven't worked.
I get the error when try to compile: py_arduino_proxy.cpp: In function ‘void setup()’: py_arduino_proxy.cpp:812: error: invalid application of ‘sizeof’ to incomplete type ‘const uint8_t []’
I think I'll stick to the _AVR_CPU_NAME_ solution...
Thanks again! Horacio
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #10 on: May 18, 2011, 03:29:21 pm » |
In the "avr_cpunames.h" several types are mapped to a single name, you can improve on that. What do you think of creating an include file "ArduinoType.h" like this? // !! the numbers are not checked !! Maybe ArduinoCapabilities.h is better name? other? // // FILE: arduinoType.h // VERSION: 0.1.00 // DATE: 2011-05-18 // PURPOSE: Specify Arduino Capabilities // // URL: http://arduino.cc/forum/index.php/topic,61528.msg445142.html#msg445142 // // HISTORY: // 2011-05-18 initial version // #ifndef ArduinoType_h #define ArduinoType_h
#include "WProgram.h" #include "avr_cpunames.h" #include "inttypes.h"
struct ArduinoType { char name[20]; uint8_t analogPins; uint8_t digitalPins; // excl analog pins that can be used as digital ones uint8_t pwmPins; uint8_t voltage; // whole part only uint16_t RAM; // KB uint16_t EEPROM; // KB uint8_t SerialPorts; uint8_t clockSpeed; // *10e6 } thisArduino =
#if defined (__AVR_AT94K__) { _AVR_CPU_NAME_ , 1, 2, 3, 5, 8, 2, 1, 8 }; // not checked
#elif defined (__AVR_ATmega328P__) { _AVR_CPU_NAME_ , 6, 13, 4, 5, 32, 2, 1, 16}; // not checked
#elif defined (__AVR_ATmega644__) { _AVR_CPU_NAME_ , 6, 13, 4, 5, 32, 2, 1, 16}; // not checked
#elif defined (__AVR_ATmega1280__) { _AVR_CPU_NAME_ , 6, 13, 4, 5, 32, 2, 1, 16}; // not checked
#elif defined (__AVR_ATmega2560__) { _AVR_CPU_NAME_ , 6, 13, 4, 5, 32, 2, 1, 16}; // not checked
// // optional more // #else { "UNKNOWN" , 0, 0, 0, 0, 0, 0, 0, 0}; // checked
#endif
#endif // ArduinoType_h // // END OF FILE
so you can do #include "avr_cpunames.h" #include "arduinoType.h"
void setup() { Serial.begin(115200); Serial.println(thisArduino.name); Serial.println(thisArduino.analogPins, DEC); Serial.println(thisArduino.digitalPins, DEC); Serial.println(thisArduino.pwmPins, DEC); // etc }
void loop() {}
|
|
|
|
« Last Edit: May 18, 2011, 03:36:14 pm by robtillaart »
|
Logged
|
|
|
|
|
Argentina
Offline
Newbie
Karma: 0
Posts: 17
|
 |
« Reply #11 on: May 18, 2011, 11:41:49 pm » |
I'll try the ArduinoType struct, validate the pins in the 'python' side, and make this validation optional (turned on by default)...
Thanks robtillaart!
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #12 on: May 19, 2011, 01:28:50 am » |
Hi Horacio,
Beware this is still not 100% but for now I think it is quite usable, once filled in it will fill the struct compile time.
Are there fields missing?
Rob
|
|
|
|
|
Logged
|
|
|
|
|
Argentina
Offline
Newbie
Karma: 0
Posts: 17
|
 |
« Reply #13 on: May 19, 2011, 07:43:13 am » |
I'd like to have one more thing: pwmPins "list" instead of the "number of". With this "list", I'd be able to validate analogWrite(). I'll try to do that, maybe with a "bitmap" (for size concerns). With a 64bits, 8 bytes array, it should be doable. Or may be less bits, since PWM support is at the "lower" pins... the Arduino Mega, Uno, etc. have PWM support on pins less or equals to 13... with 2 bytes will be fine. Something like: // // FILE: arduinoType.h // VERSION: 0.1.00 // DATE: 2011-05-18 // PURPOSE: Specify Arduino Capabilities // // URL: http://arduino.cc/forum/index.php/topic,61528.msg445142.html#msg445142 // // HISTORY: // 2011-05-18 initial version // #ifndef ArduinoType_h #define ArduinoType_h
#include "WProgram.h" #include "avr_cpunames.h" #include "inttypes.h"
struct ArduinoType { char board_name[20]; uint8_t analogPins; uint8_t digitalPins; // excl analog pins that can be used as digital ones uint16_t pwmPinsBitmap; uint8_t voltage; // whole part only uint16_t RAM; // KB uint16_t EEPROM; // KB uint8_t SerialPorts; uint8_t clockSpeed; // *10e6 } thisArduino =
#if defined (__AVR_AT94K__) { _AVR_CPU_NAME_ , 1, 2, 0x00, 5, 8, 2, 1, 8 }; // not checked
#elif defined (__AVR_ATmega328P__) { // PWM pins on Uno: 3, 5, 6, 9, 10, 11 _AVR_CPU_NAME_ , 6, 13, 0x00 | 1<<3 | 1<<5 | 1<<6 | 1<<9 | 1<<10 | 1<<11, 5, 32, 2, 1, 16}; // not checked
#elif defined (__AVR_ATmega644__) { _AVR_CPU_NAME_ , 6, 13, 0x00, 5, 32, 2, 1, 16}; // not checked
#elif defined (__AVR_ATmega1280__) { // PWM pins on Mega: 2 to 13 _AVR_CPU_NAME_ , 6, 13, 0x00 | 1<<2 | 1<<3 | 1<<4 | 1<<5 | 1<<6 | 1<<7 | 1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12 | 1<<13, 5, 32, 2, 1, 16}; // not checked
#elif defined (__AVR_ATmega2560__) { // PWM pins on Mega: 2 to 13 _AVR_CPU_NAME_ , 6, 13, 0x00 | 1<<2 | 1<<3 | 1<<4 | 1<<5 | 1<<6 | 1<<7 | 1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12 | 1<<13, 5, 32, 2, 1, 16}; // not checked
// // optional more // #else { "UNKNOWN" , 0, 0, 0x00, 0, 0, 0, 0, 0}; // checked
#endif
#endif // ArduinoType_h // // END OF FILE
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #14 on: May 19, 2011, 08:58:01 am » |
Good idea the PWM bitmap! however is 16 bits enough?
|
|
|
|
|
Logged
|
|
|
|
|
|