Cosa: An Object-Oriented Platform for Arduino programming

Anyone else having intermittent USB upload failures on OS X (Mavericks)?

Mine fails about every 4 uploads.

jediunix:
Anyone else having intermittent USB upload failures on OS X (Mavericks)?

Mine fails about every 4 uploads.

@jediunix

This may depend on the sketch. If the current uploaded sketch does not use CDC the USB port may get disconnected and you will need to push the RESET button before upload.

Cheers!

kowalski:
This may depend on the sketch. If the current uploaded sketch does not use CDC the USB port may get disconnected and you will need to push the RESET button before upload.

I am using CDC. I can monitor tracing so the device is working. This is what I see when it fails:

/Applications/Arduino-1.5.8.app/Contents/Java/hardware/tools/avr/bin/avrdude -q -V -p atmega32u4 -C /Applications/Arduino-1.5.8.app/Contents/Java/hardware/tools/avr/etc/avrdude.conf -D -c avr109 -b 57600 -P /dev/cu.usbmodem5d11 -U flash:w:./obj/build-1.5.8/adafruit-atmega32u4/AnalogClock/AnalogClock.hex:i

Connecting to programmer: .
Found programmer: Id = "

t -"; type = t
.   Software Version = 
. Hardware Version = 

avrdude: error: buffered memory access not supported. Maybe it isn't
a butterfly/AVR109 but a AVR910 device?
make: *** [upload] Error 1

(Yes, it is preceded by ard-reset-arduino)

Probably something related to OS X. I will attempt to debug further.

I'm having a strange problem that may be a bug in Timer but I could use a quick look by others to see if it is my mistake.

Source is in Dropbox - Error - Simplify your life along with the trace output.

I have one object slow (class Slow) that is a periodic timer with 1 second interval. This works fine as it continues to generate events that drive tracing every second. This object drives another, pulsar (class Pulsar (a Timer)), by making it advance some number of units every second. The number of units is known to be able to be completed within the 1 second interval because it asks pulsar how many it can complete in that time.

pulsar toggles something on/off with short intervals. When it has completed all the pulses it was asked to do the timer is not running. When it receives a request to advance it starts the timer if it isn't currently running.

Everything works as expected for ~70 minutes. Then it appears that the timer for pulsar is not firing. The trace output shows the following near the end:

intervals=4290 requested=214500 done=214450 remaining=50 Timer::s_queue.available()=2
intervals=4291 requested=214550 done=214500 remaining=50 Timer::s_queue.available()=2
intervals=4292 requested=214600 done=214550 remaining=50 Timer::s_queue.available()=2
intervals=4293 requested=214650 done=214600 remaining=50 Timer::s_queue.available()=2
intervals=4294 requested=214700 done=214650 remaining=50 Timer::s_queue.available()=2
intervals=4295 requested=214750 done=214650 remaining=100 Timer::s_queue.available()=2
intervals=4296 requested=214800 done=214650 remaining=150 Timer::s_queue.available()=2
intervals=4297 requested=214850 done=214650 remaining=200 Timer::s_queue.available()=2
intervals=4298 requested=214900 done=214650 remaining=250 Timer::s_queue.available()=2
intervals=4299 requested=214950 done=214650 remaining=44 Timer::s_queue.available()=2 ERROR=214694
TimerIssue.ino:169:assert:true == false

Normal is 50 requests every second. remaining should always be ~50. The timer queue has 2 entries - one for slow and one for pulsar. Yet it appears on_expired is no longer called. Pulsar remaining count is a uint8_t so it rolls over.

I could use another set of eyes to look at my code to verify that I'm not missing something.

Thanks!

jediunix:
I'm having a strange problem that may be a bug in Timer but I could use a quick look by others to see if it is my mistake.

...

Everything works as expected for ~70 minutes. Then it appears that the timer for pulsar is not firing. The trace output shows the following near the end:

...

intervals=4294 requested=214700 done=214650 remaining=50 Timer::s_queue.available()=2
intervals=4295 requested=214750 done=214650 remaining=100 Timer::s_queue.available()=2

NEVER MIND!

I will open an issue with Cosa. Between 4294 and 4295 seconds the microsecond count rolls over to 0.

I am making available a Cosa-based driver for 128x64 and 128x32 OLED displays based on SSD1306/SH1106 for TEXT MODE only (i.e. LCD-derived) (21 characters x 8/4 lines). These controllers support either I2C/TWI or SPI (driver is hardware-SPI only).

It has been tested on AdaFruit Monochrome OLED using I2C/TWI and SPI and on generic 128x64 using I2C/TWI.

The AdaFruit products are based on the SSD1306 device. Most generic products are based on a similar device, SH1106.

At some point in the future this may be contributed to the Cosa project. (I would welcome review for suitability.)

Available at CosaSSD1306_Text CosaOLED.

As my needs evolve I may develop a full graphics (Canvas-derived) driver for these chips.

I am trying the example CosaEEPROM on an atmega32u4. The make target 'eeprom' only resets. The make target 'raw_eeprom' says

  • On leonardo, raw_xxx operation is not supported. Stop.*

Arduino.mk raw_eeprom says ERROR_ON_CATERINA.

What's the story here? Is it not possible to initialize EEPROM on Caterina?

jediunix:
What's the story here? Is it not possible to initialize EEPROM on Caterina?

@jediunix

Did you try modifying Arduino.mk and disabling the caterina check? This seems to have to do with a problem in older versions of avrdude. Anyway allowing eeprom hex upload works but avrdude will ask if the fuses should be updated. The modification is available on github.

Cheers!

Ref. [avrdude-dev] [bug #36901] flashing Atmega32U4 EEPROM produces garbage o

Announcing the availability of more add-ons for Cosa.

CosaTimezone provides full Timezone capability.
CosaINA219 provides driver for INA219 high-side current sensor.

I wonder if the following is possible in Cosa assuming some Arduino with LAN connection:

  • Server for TCP/IP on port xxxx for direct communication with some PC
  • Small simpel webserver on port 80, serving static HTML
  • MQTT Client

This means I can't use blocking calls, but should poll the TCP ports and send MQTT data if available in the main loop or something?

@MarsWarrior

I don't see any direct problems with this usage of the Ethernet services. Obviously it would be more elegant to use interrupts from the Ethernet controller but polling is fine.

I have done some combination of services such as Telnet and Web-server. No problems with that.

Cheers!

Some information on the latest updates in the Che Cosa project. Summary of commits since mid-September.

  • Support for Arduino 1.0.6
  • Refactoring of SPI class to allow multi-tasking (i.e. device driver semaphore)
  • Update SPI device drivers
  • New LCD member function; line_clear()
  • Bugfix: NRF24L01P missing auto-ack on pipe
  • Allow Alarm period to be larger than 18 h (i.e. time period changed from uint16_t to uint32_t)
  • Add support for Arduino Micro
  • Bugfix: Time-zone should be signed
  • Bugfix: PWMPin handling for ATmega1284P
  • Allow yield during Analog conversion
  • Improved CDC auto-reset handling
  • Allow EEPROM upload to ATmega32U4 based boards
  • New Alarm member function; expired_in() to query time left before alarm
  • Add support for Arduino Pro Mini @ 8 MHz
  • Improved checking of build size; program memory, SRAM and EEPROM
  • Add support for LowPowerLab Moteino Mega
  • Bugfix: Risk for Watchdog left hanging removed
  • Initial testing of Arduino 1.6.0rc1
  • Added support for SainSmart LCD2004 I2C LCD adapter
  • Initial support for libraries and components. Command line support only
  • Many improvements to the command line build script and makefiles

Special thanks to @jediunix, @SlashDevin and @thomasahlendorf for many contributions and bug fixes.

Cheers!

The latest update include support for event driven programming of IOStream device receive and send. This may be used to allow the UART device driver to push an event when a full line has been received or when the input buffer is full.

The new template class is called IOEvent and takes an implementation of the IOStream::Device class as parameter. Below is a snippet from the example sketch CosaIOEvent.ino.

typedef IOBuffer<UART::BUFFER_MAX> Buffer;

class Handler : public Event::Handler {
public:
  virtual void on_event(uint8_t type, uint16_t value) 
  { 
    TRACE(type);
    Buffer* buf = (Buffer*) value;
    while (buf->available()) trace << (char) buf->getchar();
  }
};

Handler handler;
IOEvent<Buffer> ibuf(&handler);
Buffer obuf;
UART uart(0, &ibuf, &obuf);

void setup()
{
  uart.begin(9600);
  trace.begin(&uart, PSTR("CosaIOEvent: started"));
}

void loop()
{
  Event::service();
}

The example sketch will read an input line and push an event. The event is then dispatched by Event::service() to the handler. The handler on_event() member function prints the event type and the received line.

The basic event dispatcher allows easily integration with timed actions (Alarms, Activity), button handler, and other event driven classes in Cosa.

Cheers!

Ref. IOEvent.hh

Now and then I get the question about mapping Cosa Pin classes and member functions to Arduino/Wiring functions. There is actually an implementation of this in the sketch CosaBenchmarkPins.ino.

inline void pinMode(Board::DigitalPin pin, uint8_t mode)
{
  IOPin::set_mode(pin, (IOPin::Mode) mode);
}

inline int digitalRead(Board::DigitalPin pin)
{
  return (InputPin::read(pin));
}

inline void digitalWrite(Board::DigitalPin pin, uint8_t value)
{
  OutputPin::write(pin, value);
}

inline void digitalToggle(Board::DigitalPin pin)
{
  OutputPin::toggle(pin);
}

inline int analogRead(Board::AnalogPin pin)
{
  return (AnalogPin::sample(pin));
}

The benchmark shows the performance when using Cosa's strongly data typed board/pin definitions. Below are the results for the adaptation functions (for Arduino 1.5.8, Arduino Nano, ATmega328P, 16MHz). The result is X2-X10 faster than the standard Arduino core.

175:void loop():info:Measure the time to perform an input pin read
194:void loop():measure:digitalRead(D7):63 ns

199:void loop():info:Measure the time to perform an output pin toggle
268:void loop():measure:digitalWrite(D8, 1/0):252 ns
274:void loop():measure:digitalToggle(D8):126 ns

280:void loop():info:Measure the time to perform input pin read/output pin write
320:void loop():measure:digitalWrite(D8, !digitalRead(D7)):566 ns
325:void loop():measure:digitalRead(D7)/digitalWrite(D8,0/1):346 ns

333:void loop():info:Measure the time to perform 8-bit serial data transfer
441:void loop():measure:digitalWrite(D9/D10):9 us
450:void loop():measure:digitalWrite(D9)/digitalToggle(D10):8 us

459:void loop():info:Measure the time to read analog pin
483:void loop():measure:analogRead(A0):112 us

For more details see the Cosa Benchmarks and Board definitions.

Cheers!

CosaTM1637 provides driver for TM1637 4 digit 7 segment LED display.

I would like to create some scheduled alarms that dispatches an event each day triggering a specific function at a given time, e.g. trigger the allLightsOff() function at 22:00:00 each day. As I have understood, I could extend the Alarm class and set the period of the Alarm to SECONDS_PER_DAY in order to dispatch the event each day. I do, however, have some issues:

  • How do I make sure that the Alarm dispatches the first event at the time I want, and not just SECONDS_PER_DAY after it has been started?

  • How do I attach a function to run when the event dispatches?

Find below my idea of how to write the code.

class RepeatAlarm : public Alarm {
public:
  RepeatAlarm(uint8_t id, clock_t at, function);
  virtual void run();
private:
  uint8_t m_id;
  clock_t m_at;
  ? m_functionhandle; // How?
};

RepeatAlarm::RepeatAlarm(uint8_t id, clock_t at, fun) : 
Alarm(SECONDS_PER_DAY),
m_id(id),
m_at(at)
m_functionhandle(fun)
{}

time_t night(0);
night.seconds = 00;
night.minutes = 00;
night.hours = 22;
RepeatAlarm goodNight(1, night, allLightsOff());

void allLightsOff(){
  transmitter.broadcast(0, 0);
}

@sehlstrom

My guess is that the Cosa Activity class is more in line with what you are trying to do. Please see the example sketches.
CosaActivity.ino

BW: Cosa does not use "attach a function". Instead it is OOP with virtual member functions. By implementing the virtual member function run() and create an instance of the class you "attach a function" to run when the Alarm occurs.

Cheers!

@kowalski

Thanks for your reply!

Yes, it seams that the Activity class is more appropriate. I did however manage to do exactly what I intended using the Alarm class. See attachment AlarmTest.ino.

But as the Activity class seams to be able to do the same thing but using fewer lines of code, I am trying it out as well. I do not really get how to set the time of the first activity in a correct way. I have set the RTC to my current time and would like to have the first Activity to occur 15 seconds from the TRC start time.

class DailyActivity : public Activity {
  typedef void (*function_type)();
  public:
    DailyActivity(uint8_t h, uint8_t m, uint8_t s, void (*f)());
    virtual void run();
  private:
    function_type m_function;
};

DailyActivity::DailyActivity(uint8_t h, uint8_t m, uint8_t s, void (*f)()) :
  Activity() {
    time_t start;
    start.hours = h;
    start.minutes = m;
    start.seconds = s;
    set_time(start, 1, SECONDS_PER_DAY);
    set_run_period(1);
  }

void DailyActivity::run() {
  time_t start = time();
  cout << PSTR("DailyActivity")
     << '(' << start.hours << ':' << start.minutes << ':' << start.seconds << ')'
     << endl;
  m_function();
}

DailyActivity goodMorning(22, 12, 15, &all_ligts_on);

setup() {
  // Start serial device and use as trace iostream
  uart.begin(9600);
  cout << PSTR("AlarmTest: started") << endl;
  
  // Start the watchdog, real-time clock and the alarm scheduler
  Watchdog::begin(16, Watchdog::push_timeout_events);
  RTC::begin();
  scheduler.begin();

  // Set the RTC to a start time, i.e. current time
  time_t now(0);
  now.hours = 22;
  now.minutes = 12;
  now.seconds = 0;
  now.date = 2;
  now.month = 1;
  now.year = 15;
  start_time = now;
  cout << PSTR("Setting RTC to ") << now 
       << PSTR(" (") << start_time << PSTR(" seconds)")
       << endl;
  RTC::time(start_time);

  // Enable the alarm handlers
  goodMorning.enable();
  
  cout << PSTR("Good morning will occur in ")
          << goodMorning.time() << PSTR(" seconds")
          << endl;
}

The output is:

AlarmTest: started
Setting RTC to 2015-01-02 22:12:00 (473551920 seconds)
Good morning will occur in 0 seconds

AlarmTest.ino (2.92 KB)

ActivityTest.ino (2.32 KB)

@sehlstrom

You might need to add some code that verifies the time setting. Remember the difference between time_t and clock_t in Cosa. There is a lot of conversion going on in your code that might get things mixed up. I would debug by first verifying the setting and then the time calculation. The next step would be to verify the trigger of the Alarm/Activity. Small steps forward.

Cheers!

Thanks!

That was the problem, I used time_t instead of clock_t when I set the time of the activity. Made a few improvements in order to simplify the code.

ActivityTest.ino (2.4 KB)