Monday, May 11, 2020

Transmitting 32 bit longs between Arduinos using I2C

No soldering this time--let's get on the bench and get going!

From a previous post, I've been messing with new old stock NSA1166 bubble displays for an upcoming audio project, the "Part I" post about retro bubble displays is here.


The 70's era stuff is all cool, but who wants a ton of wires running from whatever to the display?  I want to make these bubbles respond to I2C commands, as you might see in an OLED display like the one here.  I2C has 2 wires to transmit, 3 if you include ground so not too much wiring. Like it. 

I won't go over nuts and bolts of I2C in this post; there is a ton of information all over the Internet about I2C already; you can read more about I2C protocol here--or watch this really good vid here--in short, it's a super easy way to move data between digital dookies.

This time I'll focus on one tid-bit:  I2C wants us to send bytes of data (8 bits) down the wire, one at a time, but for this project I need 24 bits on the slave side; since this 6 digit bubble can display 0-999999. How do we do that?

Wait--let's transmit 32 bits, since that will be a common need for other projects. So: my goal is to be able to enter a long value in decimal on the master Arduino and have it show up in decimal on the slave.

I set up a Proof of Concept on the bench to figure this out. An Arduino UNO was used as I2C master and ProMicro cheapo clono as I2C slave--the ProMicro uses D2 for SDA and D3 for SCL. You knew that right? I didn't. Read more about that here. The Arduino IDE gives us the extremely useful wire.h library right out of the box, greatly simplifying I2C programming so let's use that.

Here's how I wired it up:

(Somewhat unbelievably,  I couldn't find an Eagle device for a 20 pin Pro Micro anywhere so I created an eagle device (how-to for that is here)--the 20 pin ProMicro Eagle thingy you see in the schematic above. Comment if you want a copy of the lbr file for this.)



Bench Press: I used 2 old laptops each with the Arduino IDE running. The iPad on the far right is used to look up Interweb things I'm stuck on. The 3 hosts take up a lot of bench space which is hard on my OCD brain but for me I guess it works....whatever.  To get this going the I2Cscanner sketch (here) proved its worth again, I used that to make sure the physical side of everything worked before digging into creating new code.


Very little wiring for any of this.  I am not 100% sure the pull up resistors are needed, but, whatever....



Rope-a-scope: turns out my bang for the buck Siglent 1202X-E scope (8VA teardown dude vid is here) can accurately analyze and show me I2C data, which i didn't know before doing this proof of concept. This feature proved invaluable as I debugged my crap code--for Siglent users, check out the vid on  getting this going here. Wish I knew about this feature before....

A couple of reminders for myself: bytes, ints, and chars are not the same thing.  For the master code I could get sloppy with variable types, but not with the slave code. I'll have to think about that, but overall, for Arduino, bytes and chars are 8 bits; ints are 16 bits, and longs are 32 bits. Doh!

Shut up and show us the code! 

The POC code sends a sample long value to slave 10000 times over I2C then quits, which should be enough to see if the damn thing is working!


///////////////////MASTER/////////////////////////
#include <Wire.h>


// next line, put any decimal # here 0 to 999999



unsigned long int longInt = 333456;  
//on arduino unsigned long goes to 4 billion something

unsigned char byteArray[4];
   
int c = 0;


void setup() {
// put your setup code here, to run once:
Wire.begin();
//Serial.begin(9600);
}


void loop() {


 

Wire.beginTransmission(0x4);

byteArray[0] = (int)((longInt >> 24) & 0xFF) ;
byteArray[1] = (int)((longInt >> 16) & 0xFF) ;
byteArray[2] = (int)((longInt >> 8) & 0XFF);
byteArray[3] = (int)((longInt & 0XFF));


for (c = 0; c < 10000; c++)
    {
    Wire.write(byteArray, 4);
    delay(10 );
//    Serial.println("Sending");
//    Serial.println(c);
Wire.endTransmission();
    }


}


///////////////////////////////////////
//////////////////SLAVE/////////////////////////


#include <Wire.h>

long value = 0;

byte byteArray[4];

byte x = 0;



void setup() {
  // put your setup code here, to run once:
Wire.begin(0x4);
Wire.onReceive(receiveEvent);
Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
delay(100);    

}

void receiveEvent(byte)
{
  Serial.println("received");
  while(1 < Wire.available()) // loop through all but the last
  {
   
    for(int i=0; i<4; i++)
        {
         byteArray[i] = Wire.read();
        
     //    Serial.println(byteArray[i], HEX);
   
        }

  }
/////// convert back to long
delay(100);
for (int q = 0; q < 4; q++)
{
  value = (value << 8 ) + (byteArray[q] & 0xff);
}
Serial.println(value); 
}



  //////////////////


I am also working on a PCB for the I2C slave arduino, bubble display, shift registers, pull up resistor array, etc., so far it looks like this; I might send it off for fab this week.



Enough! If anyone has questions about the code, bubbles, PCB, and all else, comment away, but for now, on the bench the POC works; putting a long value in the master code will make it show up in serial out on the slave side. Joy!  For the PCB, who knows--more bubble stuff coming up, as well as an audio chaos generator, just in time for the global chaos we are going through right now. Back to soldering soon. See ya.

UPDATE 7-18-20: Aloha! got this all working, see the Part III of this post here.

No comments:

Post a Comment

Python Hardware Sequencer Part I: PNG to CV Converter

 For anyone else out there living under a rock : there's a not so new kid in town for us DIY freaks, the SBC--doh, " single board c...