Cosa: An Object-Oriented Platform for Arduino programming

Interesting observations, I know naff-all about 1-wire and am flying by data sheet here. I'll add the chip and figure it out later :slight_smile:


Rob

Hey kowalski,
First, I echo others' recent kudos for your continued great work on this platform!

Now for a hopefully simple question that I'm almost embarrassed to ask:
How do I use the additional hardware serial ports on the Arduino Mega2560?
I perused through the UART.hh and UART.cpp files, and I see code for what looks like uart1,2,3 inside of: #elif defined(ARDUINO_MEGA)
But simply calling uart1.begin(9600); didn't compile.

I did get this blip of code to compile at least (not sure if it really works or not):

static char ibuffer1[UART::BUFFER_MAX];
static IOBuffer ibuf1(sizeof(ibuffer1), ibuffer1);
static char obuffer1[UART::BUFFER_MAX];
static IOBuffer obuf1(sizeof(obuffer1), obuffer1);
UART uart11(1, &ibuf1, &obuf1);
...
uart11.begin(9600);

...but I wasn't sure if all of that was necessary if there is built-in support for simply calling: uart1.begin(9600);, etc.
Also, I had to use "uart11" as "uart1" complains about: "multiple definition of `uart1'"
Am I missing something simple?

Also, I'm curious as to the status of SoftwareSerial-like support... :slight_smile:

Thanks in advance for any help!

Thanks for the response, kowalski!

Please forgive my ignorance, but may I ask why the design principle is that the additional UART instances/buffers are not pre-allocated? Is it to save memory? If so, I would "think" that the MEGA certainly has enough memory to handle them being pre-allocated in the #elif defined(ARDUINO_MEGA) block so they can simply be used like the default uart. After all, multiple hardware UARTs was my primary reason for purchasing a MEGA :slight_smile:

Just curious... Thanks!

sirhax:
Please forgive my ignorance, but may I ask why the design principle is that the additional UART instances/buffers are not pre-allocated? Is it to save memory?

Great questions! I want to leave as much memory as possible to applications. Preallocation of buffers is actually difficult as it depends on the amount of "speed adjustment" needed. The buffer should be as large as possible to allow the application to continue processing. But small enough to leave as much memory as possible. The UART can be viewed as an extra processor. The goal is to get as many of the AVR hardware resources to run in parallel with the processor core.

An example: for a given output stream (bytes per second, in burst) the buffer size depends on the speed of the transmission (baud-rate). The faster the baud-rate the shorter the buffer for a fixed output rate but at least the size of a burst. If the buffer is too small the application will stall due to waiting on the UART.

Nearly all memory allocation in Cosa is configurable and/or input parameters as the IOBuffer's.

Cheers!

Edit: Updated with the new IOBuffer template class for better performance and improved memory allocation. There is also a new example sketch in the Cosa/Sandbox: https://github.com/mikaelpatel/Cosa/blob/master/examples/Sandbox/CosaMegaUARTs/CosaMegaUARTs.ino


Hi sirhax!

I have added some support for multiple UARTs. It's a quick fix with a support macro for the setup. The usage is as below.

static IOBuffer<UART::BUFFER_MAX> ibuf1;
static IOBuffer<UART::BUFFER_MAX> obuf1;
UART uart1(1, &ibuf1, &obuf1);
...
void setup()
{
   ...
   UART_SETUP(1, uart1);
   uart1.begin(9600);
   ...
}

The support macro UART_SETUP() is used for binding the UART instance to the ISR handler. The extern variables are now refactored (static class members) and defined in the UART.hh file. The macro does the job. I also took the opportunity to change the strange constructor parameter order of IOBuffer. Had missed that :slight_smile:

Please use the issue handling on github. It allows me and others follow these types of great improvement ideas.

Cheers!

Some news on the latest improvements:

  1. Extended Virtual Wire Interface
    The Cosa Virtual Wire Interface (VWI) has been updated with support for node addresses, address match and message types. In extended mode a header is added to the payload. The header contains the Transmitter node address, message sequence number and message type. The receiver(s) will match the address by masking and comparing with its own node address. The full message with header and message is returned to the receiver for further dispatch. The interface change is backward compatible. Please see the example sketches CosaVWItempsensor.ino, https://github.com/mikaelpatel/Cosa/blob/master/examples/VWI/CosaVWItempsensor/CosaVWItempsensor.ino, and CosaVWItempmonitor.ino, https://github.com/mikaelpatel/Cosa/blob/master/examples/VWI/CosaVWItempmonitor/CosaVWItempmonitor.ino.

  2. NEXA/Homeeasy Wireless Remote command receiver.
    Support for interrupt driven or polled wireless remote command receiving. See NEXA.hh, https://github.com/mikaelpatel/Cosa/blob/master/Cosa/Driver/NEXA.hh, and the example sketch CosaNEXAreceiver.ino, https://github.com/mikaelpatel/Cosa/blob/master/examples/Sandbox/CosaNEXAreceiver/CosaNEXAreceiver.ino.

  3. IO-vector support
    The type iovec_t (see Cosa/Types.h) is a struct with buffer/size pair. A null terminated vector of iovect_t is a convenient method of allowing scattered buffer elements that are gathered for instance on send.
    a. New VWI::send(vec) member function to allow additional buffers on send. Allows additional headers without code change.
    b. New IOStream::Device::writev(vec) member function.

  4. Template classes
    The Cosa Queue and IOBuffer classes have been refactored to template classes. This allows additional performance and improved memory allocation. An example of ripple effect; The Cosa FSM benchmark measurement of sending messages between finite-state machines was improved by 50% (20 us per message dropped to 10 us).

Cheers!

A new Cosa blog post is available. It presents the Cosa Enhanced Virtual Wire Interface; reliable wireless RF433 module communication with message acknowledgement and auto-retransmission. The VWI classes may be used with ATtinyX5.

Cheers!

Mikael,
This is really interesting what you are doing.
I have peeked into your Cosa OOP documentation over the past weeks and can see you have put a tremendous amount of work into this.
It looks looks to be a highly organised, well thought out and useable framework.

I look forward to learning more and hopefully trying it out.


Paul

Paul, thanks for your comments and encouragement.

Cosa as a project is great fun and I have lots on the agenda. It is very much a bottom-up process to build a framework and I want/need to get feedback early on to move in the right direction. Obviously more support for Wireless, Ethernet, USB, and many more sensor drivers are needed. Also the host part of Ciao/Cosa Fai is also needed before becoming really useful.

If you get the time to test Cosa I would very much appreciate feedback.

Cheers!

A new Cosa blog post is available. It presents the basic working procedure for getting Arduino Cosa sketches working on ATtiny. Special focus is on how to use the Cosa Soft UART and PCD8544 LCD driver for trace output from ATtiny.

Cheers!

Just stumbled onto this. Nice lib!

I am writing a modest lib myself that is only for advanced users and targets to minimize ram usage (at the expense of using more prog-mem) and allow total control. I got frustrated with the stupid Arduino lib implementations that seem to be the standard -they are great for demo's and simple prototypes, but will fight you when you want to do some real work. I also use mainly template classes and no virtual functions to accomplish my goals. I intentionally leave out/do not use certain OO/C++ features in order to keep it small.

What do you think of this (to me, this looks very logical):
ViewPort<TextLcd<LcdDriver<..pin io..>, ..physical size..>, ..virtual size..>

This demonstrates:

  • separation of concerns: the actual hardware driver can be swapped out for different types of LCDs, or any one of the template classes
  • extensablility: if you don't like the impl. of some part, replace with your own.
  • No ram: all parameters are compiled into the code and the template classes do not use virtuals. This assumes you will not add/change LCD displays dynamically.

To avoid using virtuals, I use this a lot:

template<BaseT>
class SomeService : BaseT
{
public:
  void SomeMethod()
  {
     BaseT::MethodOnBaseT();
  }
};

Currently I still depend on the Arduino lib, but I'm planning to replace that.

Anyway, all the best with your library.

The library looks nice, but I don't get how I can subscribe to an event (for example of an ExternalInterruptPin) when subclassing is not an option? The class pushes an event but I do not see how I it can be handled from outside.

Ferio:
...how I can subscribe to an event (for example of an ExternalInterruptPin) when subclassing is not an option? The class pushes an event but I do not see how I it can be handled from outside.

Hi Ferio. Thanks for your interest in this project. The short answer to your question is that you can always handle the event directly instead of calling dispatch(). Below is snippet from CosaFSMBlink which uses the dispatch-method.

void loop()
{
  // The basic event dispatcher
  Event event;
  Event::queue.await(&event);
  event.dispatch();
}

And below is a snippet from the CosaNEXAreceiver sketch which handles the event instead of calling the on_event() method.

void loop()
{
  // Wait for the next event
  Event event;
  Event::queue.await(&event);
  uint8_t type = event.get_type();
  Event::Handler* handler = event.get_target();

  // Check that the event is an read completed and from the correct source
  if ((type != Event::READ_COMPLETED_TYPE) || (handler != &receiver)) return;
  ...
}

The flow of control is:

  • The event.await() will perform a sleep while waiting for events
  • The ExternalInterruptPin::on_interrupt() method is called when an interrupt occurs. The default implementation will push a changed event onto the event queue
  • The event.await() returns with the event
  • If event.dispatch() is called the event target on_event() method is called, otherwise decode the event type and target

You can find more details about this on the blog, Cosa: The Analog Pin Classes; An Introduction to Event Driven Programming and Cosa: Object-Oriented Interrupt Handling

Cheers!

[quote author=obiwanjacobi link=topic=150299.msg1227898#msg1227898 date=1367654559]
Just stumbled onto this. Nice lib!

Thanks obiwanjacobi I had a quick look at your library. Many interesting ideas! While I started off not using templates, your approach is not using virtual.

I have introduced a few templates for some of the basic classes (such as Queue and IOBuffer) to allow additional compiler optimization and strangely higher performance. I use virtual mainly for callbacks and abstract interfaces with code reuse.

Using functions as callbacks often requires additional parameters and state. In these cases an instance/object is a nice way to handle this. It scales better than callback functions and global variables. Virtual also allows reuse of a lot of code and reduces the program memory footprint. A good example is the IOStream::Device class and the default implementation which is reused many time by both IOBuffer, UART, SUART, etc. The extra SRAM cost for the virtual table handling is only 2 bytes (per instance). For 20-40 instances this is only 40-80 byte in total. If the target is a ATtiny with less than 0.5Kbyte then this might seem a lot.

Have you seen Rick Kimball's library https://github.com/RickKimball/msp430_code. This is a full fledged template based library. And uses a lot of the template tricks. Very modern and nice coding style.

Cheers!

Thanks for the link, didn't know that lib. I will look into it.

Although my code is not as elaborate as your lib (yet! XD) I have not found any need to have virtuals. I basically put the class hierarchy upside down and that eliminates the need for virtuals - also eliminates polymorphism. My reasoning is that in small embedded systems you have more use for control than abstraction (so not for beginners). As in the LCD example I gave previously, I am also looking to separate concerns/logic. The logic of how to write to an LCD display is always the same (for the same type of LCD displays). But the way you hooked it up to your MCU can differ for each design (directly on the pins, shift regs, etc). This approach leaves the door open to change the driver code and still reuse the LCD code, something I have never seen in other libs.

I am currently testing a way for lightweight cooperative mutlitasking. Once I figured out how to do async time delays I can go to town on supporting other typical devices (I2C, LCD etc).

Thanks for the fast answer. Handing the event in the dispatch loop would become a bit messy on the long run.

Just to depict my problem: I'm planning to use a rotary encoder which uses two pins. Therefore subclassing the ExternalInterruptPin as in the IR::Receiver example is not possible. So probably I would have to subclass the pin in order to be able to delegate the event handling to some other class/function.

But I'm in the moment just looking into different lib to see which would fit my needs best. Yours looked quite interesting.

kowalski:
The short answer to your question is that you can always handle the event directly instead of calling dispatch(). Below is snippet from CosaFSMBlink which uses the dispatch-method.

Ferio:
Thanks for the fast answer. Handing the event in the dispatch loop would become a bit messy on the long run.

Just to depict my problem: I'm planning to use a rotary encoder which uses two pins. Therefore subclassing the ExternalInterruptPin as in the IR::Receiver example is not possible. So probably I would have to subclass the pin in order to be able to delegate the event handling to some other class/function.

You are welcome! This is an interesting design problem.

The solution you are sketching is similar to the pattern used in the NRF24L01P driver where a sub-class of ExternalInterruptPin is defined within the class itself and posts events back to the "parent", i.e. container.
Below is a snippet from the driver.

class NRF24L01P : private SPI::Driver {
private:
  ...
  class IRQPin : public ExternalInterruptPin {
    friend class NRF24L01P;
  private:
    NRF24L01P* m_nrf;
  public:
    IRQPin(Board::ExternalInterruptPin pin, Mode mode, NRF24L01P* nrf) :
      ExternalInterruptPin(pin, mode),
      m_nrf(nrf)
    {}
    virtual void on_interrupt(uint16_t arg = 0);
  };
  IRQPin m_irq;
  ...
};

void
NRF24L01P::IRQPin::on_interrupt(uint16_t arg)
{
   ...
   Event::push(Event::RECEIVE_COMPLETED_TYPE, m_nrf, status);
   ...
}

You could have several InterruptPins defined within a container class which post events to the same target object (i.e. the container).

Cheers!

obiwanjacobi:
Thanks for the link, didn't know that lib. I will look into it.
...
I am currently testing a way for lightweight cooperative mutlitasking. Once I figured out how to do async time delays I can go to town on supporting other typical devices (I2C, LCD etc).

You are welcome. Here are a few links to Proto-threads which is a style of implementing coroutines. These are actually from the comment block in Cosa/Thread.hh, http://dl.dropboxusercontent.com/u/993383/Cosa/doc/html/d0/d51/classThread.html and https://github.com/mikaelpatel/Cosa/blob/master/Cosa/Thread.hh.

[1] Adam Dunkels et al, Protothreads: Simplifying Event-Driven Programming of Memory-Constrained Embedded Systems, SenSys'06, November 1-3, 2006, Boulder, Colorado, USA.
[2] Larry Ruane, protothread: An extremely lightweight thread library for GCC, Google Code Archive - Long-term storage for Google Code Project Hosting.
[3] Protothread - Wikipedia

It is possible to avoid the "switch" problem by using gcc local labels and label address. The Cosa implementation is object-oriented and fully integrated with the Cosa periodic timer queue.

Cheers!

Nice docs!

That does look like a good implementation. Most threading impl's I've seen are over the top for the small MCU they're supposed to work on (IMHO).

I will stick with my macro's and switch statement (two bytes) for a while and see if I can get it to work. My aim is to allow the tasks to run with and without a scheduler. The scheduler only handles waiting tasks, at least that's the idea.

Thanx!

A new Cosa blog post is available. It presents the Cosa NEXA driver; RF433 transmitter and receiver for Home Automation with NEXA/HomeEasy remote and receivers. Driver and example sketches may be run on ATtiny.

Cheers!