Hello, I am doing a project where I require to send up to 4 bytes of data maximum (32 bits) and require to send the bit count before sending the data over I2c. Once this data is sent from the uno to the Attiny it will use it to control a peice of hardware. I have tried using wire.h on the uno as a master and the Attiny as a slave using tinywires.h but to no avail. If anyone could give me any pointers how to use these successfully.
At the moment I'm splitting the 4 byte data into separate bytes and trying to send them using wire.starttransmission(attinyaddress) then using wire.send(dataarray[arraynumber]) in a for loop and finalising it with wire.endtransmission.
On the attiny I'm trying to receive it using tinywires.receieve.
This is my first time using I2c so any pointers would be fantastic. Sorry for the talking through my code other than my actual code. I'm currently away from my machine and can't get this off my mind.
Thank you.
Any code used or do You controll Your two controllers by Your thoughts?
It should work like that, been there done that. Without code & circuit diagram (grounds connected? Pull-up resistors in place?) no idea what you're doing wrong.
Also what core are using for the ATTiny?
As I said in my post I do apologise for the no code. Im using pull up resistors of 10kOhm on 5V Vcc for the SDA and SCL.
ATTiny Code
/*
// I2C
arduino pin 0 = not(OC1A) = PORTB <- _BV(0) = SOIC pin 5 (I2C SDA, PWM)
arduino pin 2 = = PORTB <- _BV(2) = SOIC pin 7 (I2C SCL, Analog 1)
// Timer1 -> PWM
arduino pin 1 = OC1A = PORTB <- _BV(1) = SOIC pin 6 (PWM)
arduino pin 3 = not(OC1B) = PORTB <- _BV(3) = SOIC pin 2 (Analog 3)
arduino pin 4 = OC1B = PORTB <- _BV(4) = SOIC pin 3 (Analog 2)
*/
// Get this from https://github.com/rambo/TinyWire
#include "TinyWireS.h"
#include <math.h>
// The default buffer size, Can't recall the scope of defines right now
#ifndef TWI_RX_BUFFER_SIZE
#define TWI_RX_BUFFER_SIZE ( 16 )
#endif
bool recievedCount = false; //Bool for byte count receieved
int bitmessagesize; //Bit count of message
int messagesize; //Byte count of message.
int messagearray[4]; //Byte array
int bytecount = 0; //How many bytes recieved
int finalmessage;
void setup() {
TinyWireS.begin(2);
mySwitch.enableTransmit(3);
}
//Recieve Wire Messages
//1. How many bits (calculate how many bytes to recieve)
//2. Bytes[0]...Bytes[N]. - Note on final byte the end non used bits must be 0
//3. Iterations
//Put together final message.
//Send via RF.
void loop() {
//Do not use delay() use tws_delay()
if(TinyWireS.available()){
//Check if ByteCount has been recieved
if(recievedCount == false){
bitmessagesize = TinyWireS.receive();
float howmanybytes = bitmessagesize / 8;
messagesize = round(howmanybytes);
recievedCount = true;
}
else if(recievedCount == true){
//Recieve Bytes
if(messagesize > bytecount){
messagearray[bytecount] = TinyWireS.receive();
bytecount++;
}
//Create final message and transmit
else{
recievedCount = false;
//Create final message
for(int x=0;x<=messagesize;x++){
finalmessage = messagearray[x] << ((messagesize * 8) - x);
}
int shiftdown = ((messagesize * 8) - bitmessagesize);
finalmessage >> shiftdown;
//USE FINAL MESSAGE OFC
}
}
}
}
Uno Code
//#include <Wire.h>
#include <stdio.h>
#include <math.h>
//#include <iostream>
//#include <cstring>
void transmitmessage(char* message);
void setup() {
// put your setup code here, to run once:
Wire.begin();
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
transmitmessage("001100110011");
}
void transmitmessage(char* message) {
int bits = strlen(message);
int bytes = round(bits / 8);
int bytecount = 0;
int bytearray[5];
for (int x = 0; x <= bytes; x++) {
for (int y = 0; y <= 8; y++) {
if ((8 * x) > bits) {
bytearray[x] |= 0 << y;
}
else {
if (message[y + (8 * x)] == '0') {
bytearray[x] |= 0 << y;
}
if (message[y + (8 * x)] == '1') {
bytearray[x] |= 1 << y;
}
}
}
}
Wire.beginTransmission(2);
Wire.write(bits);
Wire.endTransmission();
for (int z = 0; z <= bytes; z++) {
Wire.beginTransmission(2);
Wire.write(bytearray[z]);
Wire.endTransmission();
// Serial.println(bytearray[z]);
}
}
As I said I do apologise since this is my first time trying to use I2C and on the ATTiny its just getting my head around it.
The library used for ATTiny45 is GitHub - rambo/TinyWire: My modifications to TinyWire Arduino libs
wvmarle:
It should work like that, been there done that. Without code & circuit diagram (grounds connected? Pull-up resistors in place?) no idea what you're doing wrong.
They are all on the same ground. The ATTiny is powered by an Uno. I program the ATTiny through an ICSP programmer to breakout board adapter. Tested it with blink to check if programming correctly.
I recall creating an onReceive() function, that gets called upon the receipt of any communication. Looks much easier to implement than what you're doing. I forgot which TinyWireS library I used, may be another one than yours.
Also I have a second function to allow the host to get data back from the Tiny. That can also help you to debug: just copy back the data to the host, so at least you know what is received is the thing you sent.
I noticed you use int where you say it's bytes - use the correct data type at those places. Saves memory and it behaves as it should without potential pitfalls when types get converted.
Thank you very much, I will look into using that function.
Be aware that onReceive and the send equivalent (onSend I think it's called) are ISRs - called by interrupt. So read the data, store it in variables, set flags for loop() to take care of it, and remember to make all those variables volatile.
So also make sure these two functions don't do anything more than they should, or try to do anything related to interrupts.
alexsketchees:
Hello, I am doing a project where I require to send up to 4 bytes of data maximum (32 bits)
You can do that with a bit count and bits in an unsigned long.
You can set the bits in binary or hex and forget all that fumbling with text,
unsigned long thirtytwobits = 0b001100110011; // binary
byte bitcount = 12;
byte bytestosend = (bitcount + 7) / 8;
Send that as 2 bytes. The unsigned long is stored as low byte to high byte,
char * bytedataaddr = (char *) &thirtytwobits; // pointer to the unsigned long cast into a char pointer
char bytedata = *bytedataaddr; // got the low byte at the pointer
.... send it
bytedata = *(bytedataaddr + 1); // got the next byte, you might use a loop
Would this be a better implementation so far. I will look into the high byte and low byte as mentioned above after this but this is what I came up with before coming back to the thread.
Uno
#include <Wire.h>
#include <stdio.h>
#include <math.h>
//#include <iostream>
//#include <cstring>
void transmitmessage(char* message);
void sendData(char cmd, int bite);
void setup() {
// put your setup code here, to run once:
// Wire.begin();
Serial.begin(9600);
transmitmessage("0011101111010101010111110000011101");
/* 00111011 - 59
11010101 - 213
01011111 - 95
00000111 - 7
01000000 - 64
*/
}
void loop() {
// put your main code here, to run repeatedly:
}
void transmitmessage(char* message) {
int bits = strlen(message);
int bytes = round(bits / 8);
int bytecount = 0;
byte bytearray[5];
for (int x = 0; x <= bytes; x++) {
Serial.print("Byte: ");
Serial.print(x);
Serial.print("\n");
for (int y = 0; y <= 7; y++) {
if (message[(8 * x) + ((y - 7) * -1)] == '0') {
//bytearray[x] |= 0 << (y);
bitClear(bytearray[x], y);
Serial.print(0);
}
else if (message[(8 * x) + ((y - 7) * -1)] == '1') {
//bytearray[x] |= 1 << (y);
bitSet(bytearray[x], y);
Serial.print(1);
}
else if (message[(8 * x) + ((y - 7) * -1)] != '1' || '0') {
//bytearray[x] |= 0 << y;
bitClear(bytearray[x], y);
Serial.print(0);
}
}
Serial.print("\n");
}
Serial.println(bits);
Serial.println(bytearray[0]);
Serial.println(bytearray[1]);
Serial.println(bytearray[2]);
Serial.println(bytearray[3]);
Serial.println(bytearray[4]);
Serial.println(3);
sendData('A', bits);
sendData('B', bytearray[0]);
sendData('C', bytearray[1]);
sendData('D', bytearray[2]);
sendData('E', bytearray[3]);
sendData('F', 3);
}
/*
Char A - How many bits
Char B - Byte 1
Char C - Byte 2
Char D - Byte 3
Char E - Byte 4
Char F - Iterations
*/
void sendData(char cmd, int bite) {
Wire.beginTransmission(0x08);
Wire.write(cmd);
Wire.write(bite);
Wire.endTransmission();
Wire.requestFrom(0x25, 1); //Check all recieved - If Success - 'T', If unsuccessful - 'F'
while (Wire.available())
{
char c = Wire.read();
if (c == 'T') {
Serial.println("Successfully Transmitted");
}
else {
Serial.println("Error in transmission");
}
}
}
ATTiny
/*
// I2C
arduino pin 0 = not(OC1A) = PORTB <- _BV(0) = SOIC pin 5 (I2C SDA, PWM)
arduino pin 2 = = PORTB <- _BV(2) = SOIC pin 7 (I2C SCL, Analog 1)
// Timer1 -> PWM
arduino pin 1 = OC1A = PORTB <- _BV(1) = SOIC pin 6 (PWM)
arduino pin 3 = not(OC1B) = PORTB <- _BV(3) = SOIC pin 2 (Analog 3)
arduino pin 4 = OC1B = PORTB <- _BV(4) = SOIC pin 3 (Analog 2)
*/
// Get this from https://github.com/rambo/TinyWire
#include "TinyWireS.h"
#include <math.h>
// The default buffer size, Can't recall the scope of defines right now
#ifndef TWI_RX_BUFFER_SIZE
#define TWI_RX_BUFFER_SIZE ( 16 )
#endif
void recieveEvent(int howMany);
bool recievedCount = false; //Bool for byte count receieved
int messagesize; //Bit count of message.
byte messagearray[5] ; //Byte array
int iterations = 0;
bool received = false;
void setup() {
TinyWireS.begin(0x08);
TinyWireS.onReceive(recieveEvent);
TinyWireS.onRequest(requestEvent);
}
void loop() {
TinyWireS_stop_check();
}
void recieveEvent(int howMany) {
//Do not use delay() use tws_delay()
if (howMany < 1)
{
// Sanity-check
return;
}
if (howMany > TWI_RX_BUFFER_SIZE)
{
// Also insane number
return;
}
if (TinyWireS.receive() == 'A') {
messagesize = TinyWireS.receive();
received = true;
}
if (TinyWireS.receive() == 'B') {
messagearray[0] = TinyWireS.receive();
received = true;
}
if (TinyWireS.receive() == 'C') {
messagearray[1] = TinyWireS.receive();
received = true;
}
if (TinyWireS.receive() == 'D') {
messagearray[2] = TinyWireS.receive();
received = true;
}
if (TinyWireS.receive() == 'E') {
messagearray[3] = TinyWireS.receive();
received = true;
}
if (TinyWireS.receive() == 'F') {
int iterations = TinyWireS.receive();
received = true;
}
}
void requestEvent()
{
if(received == true){
TinyWireS.send('T');
received = false;
}
else{
TinyWireS.send('F');
}
}
Honestly though guys thanks for all of your help. You have been fantastic.
The forum just killed my reply, even got an error 404.
The best way I know to be sure of what bits in a variable are set is to use hexadecimal to set and print the variable.
unsigned long abc = 0x04030201; // 4 bytes in a row, from low to high order they are 1, 2, 3, 4
Serial.println( abc, HEX ); // should print 4030201
You could go binary but it's a lot harder to check if you know hex, a real time-saver.
abc = 0b00000100000000110000001000000001; // same as 0x04030201
The TinyWire.receiver() command reads the byte, the next call reads the next byte. You normally store this value in a variable for processing, or if you want to receive a certain data set you read the several bytes into a single variable and test that.
if (TinyWireS.receive() == 'A') {
messagesize = TinyWireS.receive();
received = true;
}
If the first byte read is 65 ('A'), a second byte will be read and placed in the variable messagesize, and then the subsequent if statement reads what is by now the third byte. Or if it's not 65, the next if statement will read the second byte that was received.
Doesn't sound right to me.
You don't need this 'A' thing. You get a number of bytes, and you know exactly what is what (as that's your protocol).
Byte 0: the data size.
byte 1 onwards: the data (no extra markers needed). Just data, byte by byte.
This is also ugly:
void sendData(char cmd, int bite) {
Why calling an int "bite" suggesting it's a byte, while it's an int, but it actually ought to be a byte as that's how you use it. Do something like:
void sendData(char cmd, byte data) {