Pages: [1] 2   Go Down
Author Topic: Dynamically get the model of Arduino  (Read 1860 times)
0 Members and 1 Guest are viewing this topic.
Argentina
Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

Code:
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:

Code:
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

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 168
Posts: 12430
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

Code:
#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

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

0
Offline Offline
Sr. Member
****
Karma: 0
Posts: 360
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 168
Posts: 12430
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

@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.

Code:
#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

Code:
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

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Argentina
Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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):

Code:
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 Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I've tested digitalPinToPort() and it isn't working...

Code:
    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 :-(

Code:
./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 Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Returning _AVR_CPU_NAME_ to Python was very easy to implement. Will use that at least for doing some controls. Thanks again!

Code:
./bin/ipython_session.py /dev/ttyACM0
(...)
Enter 'quit()' to exit.

In [1]: proxy.getAvrCpuType()
Out[1]: 'ATmega328P'
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 168
Posts: 12430
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


digitalPinToPort(pin) is a macro reading from array/memory, any number greater than the arraysize is meaningless as it reads from somewhere.
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Argentina
Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 168
Posts: 12430
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Code:
//
//    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

Code:

#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

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Argentina
Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 168
Posts: 12430
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Argentina
Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

Code:
//
//    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

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 168
Posts: 12430
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Good idea the PWM bitmap! however is 16 bits enough?
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Pages: [1] 2   Go Up
Jump to: