Go Down

Topic: Cosa: An Object-Oriented Platform for Arduino programming (Read 104217 times) previous topic - next topic

kowalski

I'm not finding support for I2C LCD - the classic 20x4 "Blue" with I2C board.  I've seen references to I2C LCD but I'm confused about where/how.  Thanks
Please see the LCD examples. The CosaLCDclock.ino might be the best to start with. The Cosa LCD support handles multiple devices and multiple adapters. Below is the class hierarchy for LCD devices.



The adapter handle the wire level connection to the LCD. They range from parallel to I2C and SPI but all connect to the LCD device.
classHD44780.html#nested-classes
classLCD.html

Another important aspect is that the LCD devices are IOStream devices and can be used together with IOStream and Trace.

The I2C adapter classes are named after the I2C port expander products; DFRobot, MJKDZ, GYIICLCD, etc.

Below is the typical structure:
Code: [Select]

// Select the access port for the LCD; Parallel 4-bit, Shift-Register, SPI, I2C
// HD44780::Port4b port;
// HD44780::SR3W port;
// HD44780::SR3WSPI port;
// HD44780::SR4W port;
HD44780::MJKDZ port;
// HD44780::GYIICLCD port;
// HD44780::DFRobot port;
// HD44780::ERM1602_5 port;

// Bind the port to the lcd
HD44780 lcd(&port, 20, 4);

// Bind the lcd to an io-stream
IOStream cout(&lcd);


Cheers!

jediunix

Please see the LCD examples.
Thanks for the info.  I was able to get it working.

For others looking, boards with "LCM1602 IIC V1" on the back work with HD44780::DFRobot.

kowalski

For others looking, boards with "LCM1602 IIC V1" on the back work with HD44780::DFRobot.
@jedjunix
I will add an alias for that I2C adapter. Thanks for the info.

Cheers!

jediunix

The hardware and software UART implementation will call the input buffer (IOStream::Device) member function putchar() when data is received. It is possible to implement this interface/abstract class instead of the IOBuffer that is normally used. The requirement is that the member function must be able to be called from an interrupt service routine (ISR).
I must not understanding something obvious here.

I have my GPS implementation working well with available()/getchar() and now I want to drive it directly from input interrupt.  I have subclassed from IOStream::Device and changed my consumer to be driven by putchar().

My implementation also needs to send commands to the GPS device.  I don't understand how to implement this correctly.  The UART must be constructed referring to my class as the input stream.  My class needs to be constructed with an output stream.  It is currently working with the application code containing:

Code: [Select]

Soft::UAT gps_output_uart(Board::D11);
GPS_NMEA_MT3339 gps(&gps_output_uart);
Soft::UART gps_uart(Board::D11, Board::PCI6, &gps);


This doesn't feel right for obvious reasons.  This approach wouldn't work for the hardware UART.

It would be desirable to have an example in Cosa.  In the meantime, could you provide some hints on how you intend this type of thing to work?

Thanks

TribesTom

Hello Kowalski,


I have seen an intersting chip : MCP23017, It's a I2C 16 port i/o expander.


Could you implement it in Cosa ? I'm not enough skillfull to this as clean and efficent like you.


Here is all info on this chip :


Adafruit info with datasheet :

http://www.adafruit.com/products/732

Official library :

https://github.com/adafruit/Adafruit-MCP23017-Arduino-Library



This chip is similar to PCF8574.


If you need some help to test or anything else that I can do, let me know.

Thanks and have a nice day.

Ps: Sorry for my bad english I'm french
Ps2: If you preffer I could make a request on GitHub. But I must find my password first.

/dev

@jediunx,

> I have my GPS implementation working well with available()/getchar() and
>  now I want to drive it directly from input interrupt...


Did you receive a Personal Message from me?  If not, here's what I sent:

I saw your comment about GPS, and I was about to point you to a thing that **tht** and I have worked on.

He started the process by creating an IOStream::Device that gets hooked to the UART.  As characters come in, the device gets called from the InterruptHandler to process one character, much like your consume.  He started out with a ublox-specific class, which I separated out into two classes: generic NMEA, and specific ublox.  The NMEA class is mistakenly called NeoGPS. I really should fix that...  :-[

Many GPS devices support multiple protocols, so I tried to be careful and allow for future derived classes.  If you're interested, you can find what we have so far over here.

If you've seen TinyGPS, you'll recognize some of those techniques.  The goal was a small RAM footprint, and the option to run without float support.  For example, if you're just logging information, there's no reason to use float because you never compute anything like a distance between two lat/longs.

I'm proud to say that a generic instance takes only **41 bytes**.  No buffers are used; each byte is consumed as it is received.  That's about 1/3 the size of a TinyGPS instance!  And that's why you see all the structs (with methods?!?), unions and bitfields.  They're really packed in there!  The ublox instance is only slightly larger at 54 bytes.

This Cosa class is different from TinyGPS (and most Arduino code) in that it is event-oriented instead of polled, like with available and getchar.  (After thinking about it, there is a way to use it in a polled way.1)

> I have subclassed from IOStream::Device and
> changed my consumer to be driven by putchar().


Sounds good!  Because you want to handle the chars in the interrupt context of putchar, you can't do very much work.  (Which is a little confusing because we're actually getting a char from the device... o_O)  That's why putchar calls Event::push when a complete NMEA sentence is received and the CRC is correct.

Essentially, it puts an Event structure in a queue of things to handle a little bit later, after we're out of the interrupt routine.  You can look at that queue in the main loop, where you can take all the time you want.  Well, by "look at the event", I mean "dispatch" the events to the objects that care about them:

Event event;
while (Event::queue.dequeue( &event ))
    event.dispatch();


These events will find their way to the on_event method of the object that's supposed to get "kicked": the object then "handles" that event.

Because NeoGPS doesn't know what your specific needs are, you must derive your own class from NeoGPS and implement your own on_event.  In general, that's how you handle events in Cosa.

I haven't checked in an example app yet, but here's the basic idea.  First, derive from the NMEA class:
Code: [Select]
class MyGPS : public NeoGPS {
public:

    virtual void on_event ( uint8_t type, uint16_t value )
    {
          if ((NMEA_GGA == (nmea_msg_t)value) ||
              (NMEA_RMC == (nmea_msg_t)value)) {

              // Do something with the fix() data member, quickly, or the interrupt routine
              //    will start replacing data as more characters are received...
              // Copy it to a local_fix if you need more time.
          }
    }
};


Then make an instance of your class and hook it to the receiving UART:

extern UART uart1; // forward declaration of uart1
MyGPS gps( &uart1 ); // need back pointer to the UART so we can also send commands

static IOBuffer<UART::BUFFER_MAX> obuf;
UART uart1(1, &gps, &obuf); // normal forward pointer to the GPS device


There is a little syntactic monkey business going on here...  The first extern "promises" that there is a real uart1 somewhere in all the code.  Usually that means it's in a different file, but in this example, it's only two lines later.  This is because gps and uart1 point at each other.  Neither declaration could be first without needing to use the other.  So, we throw in the forward declaration for one (uart1), then really declare the second object (gps), and finally really declare the first (uart1).  The second uart1 declaration is how uart1 knows to call gps.putchar from the InterruptHandler.  The first gps declaration is how gps knows to call uart1.putchar with transmitted data.

Normally, the IOStream::Device has no way to find the IOStream (UART) that called it.  That's why there's a TX member in NeoGPS.  When gps.send is called, it must use the TX member for the UART to send the data.  Does that answer your question:

> My implementation also needs to send commands to the GPS device.
> I don't understand how to implement this correctly.


BTW, if you didn't need to send anything to the GPS device, then NeoGPS wouldn't need a back pointer to the receiving UART.

> The UART must be constructed referring to my class as the input stream.  My
> class needs to be constructed with an output stream.  It is currently working with
> the application code containing:
>
> Soft::UAT gps_output_uart(Board::D11);
> GPS_NMEA_MT3339 gps(&gps_output_uart);
> Soft::UART gps_uart(Board::D11, Board::PCI6, &gps);[/tt]
>
> This doesn't feel right for obvious reasons.  This approach wouldn't work for the hardware UART.


Hmmm, I think you just need to make the gps_output_uart a forward declaration of gps_uart:

extern Soft::UART gps_uart;
GPS_NMEA_MT3339 gps(&gps_uart);
Soft::UART gps_uart(Board::D11, Board::PCI6, &gps);


I'll try to get that test program up there Real Soon Now.

One other note: there are also struct methods related to fusing multiple received sentences into one coherent fix.  As you know, NMEA must report several sentences before you have all of time, position (lat/lon/alt) and velocity (course/heading) data.  The gps_fix_t::operator |= helps with that fusing process.  This makes it easier to have a merged fix that accumulates the different pieces you get from different NMEA sentences.

I hope this helps!

Cheers,
/dev


1  You could just use the regular (or Soft) uart instance:

static IOBuffer<UART::BUFFER_MAX> ibuf;
static IOBuffer<UART::BUFFER_MAX> obuf;
UART uart(0, &ibuf, &obuf);

MyGPS gps( &uart );


Then your main loop would do something like:

while (uart.available())
  gps.putchar( uart.getchar() );


This will still generate events, even though the chars are not processed in the InterruptRoutine.  They're held in obuf until you get them.  The back pointer to uart allows you to use gps.send methods.

kowalski

@jediunix

This seems to be a question about C++ and forward declaration? You need to forward declare the gps or the soft uart class instances/variables. If you look at the definition of uart in the header file you will find an example of how to do this. https://github.com/mikaelpatel/Cosa/blob/master/cores/cosa/Cosa/IOStream/Driver/UART.hh#L279

Cheers!

BW: Seems to be a nice project. Please publish more code.

UPDATE: Seems like /dev was both faster and gave a in-depth answer.

kowalski

I have seen an intersting chip : MCP23017, It's a I2C 16 port i/o expander.
Could you implement it in Cosa ? I'm not enough skillfull to this as clean and efficent like you.
@TribesTom

Thanks for your interest in Cosa. I have a very simple rule; I do not implement any device drivers that I cannot test and at this time I do not have any board with the MCP23017. Also I try to create a software interface that makes the device easy to use and it should also have some type of challenge. In this case the MCP23017 can generate interrupts on pin change. It also has a weak pull-up.

Cheers!

jediunix

@/dev and @kowalski,

Doh!  It was so simple I couldn't see it!  I guess I'm rustier than I thought.

My simplistic GPS stuff is http://github.com/jediunix/CosaGPS. I discovered NeoGPS after I had most of mine working.  I'm using MT3339 for GPS - on the AdaFruit breakout board.

At some point it would be nice if Cosa contained GPS support.

My current project is to modify a large (32"/91cm) wall clock to GPS with automatic savings time.  Ours hangs very high on the wall.  Battery powered.

I'm happy to see that Timer is pretty accurate for time keeping (Periodic not so much).  I have it expiring every 500ms because the clock coil needs push/pull every second.

jediunix

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

Mine fails about every 4 uploads.

kowalski

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!

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.
I am using CDC.  I can monitor tracing so the device is working.  This is what I see when it fails:

Code: [Select]
/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.

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.

Source is in https://www.dropbox.com/sh/owfpumd0xujs8pn/AABnWs0iaE-xnutnM4Rbpwzua?dl=0 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.

jediunix

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 https://github.com/jediunix/CosaSSD1306_Text.


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

Go Up