I have an issue related to "Transmission" from microcontroller. Microcontroller is able to receive, but unable to transmit.
this is an additional issue to my previously asked question [here]
Below is the max485 interface with microcontroller :
Here is my code snapshot :
// RS485
TRISBbits.TRISB6 = INPUT_PIN; // RX - RB6/RP38 PIN<42>
TRISBbits.TRISB7 = OUTPUT_PIN; // TX - RB7/RP39 PIN<43>
TRISBbits.TRISB8 = OUTPUT_PIN; // !RE/DE Control Pin RB8/RP40 PIN<44>
// RS485 Config
#define RS485_TX PORTBbits.RB6 // RS485 Transmitter
#define RS485_RX LATBbits.LATB7 // RS485 Reciever
#define RS485_CTRL LATBbits.LATB8 // RS485 Control Pin
// UART ISR
void __attribute__((interrupt, no_auto_psv)) _U4RXInterrupt(void)
{
rs485Char = U4RXREG;
RS485_CTRL = 1; // Enable driver
U4TXREG = rs485Char;
RS485_CTRL = 0; // disable driver RE/DO
}
void InitRs485(void){
// configure U1MODE
U4MODEbits.UARTEN = 0; // Bit15 TX, RX DISABLED, ENABLE at end of func
U4MODEbits.URXINV = 1; // 1:URXINV Idle state is '0' ; 0=UxRX Idle state is '1';
U4MODEbits.ABAUD = 0; // Bit5 No Auto baud (would require sending '55')
U4MODEbits.BRGH = 0; // Bit3 16 clocks per bit period
U4MODEbits.PDSEL = 0; // 0 : 8 bit,no parity; 1 : 8 bit,even parity; 2 : 8 bit,odd parity; 3 : 9 bit,no Parity
U4MODEbits.STSEL = 1; // 1 : 2 Stop bits; 0 : 1 Stop bits
U4MODEbits.LPBACK = 0; // Lookback disable
U4STAbits.URXISEL = 0; // Interrupt flag bit is set when a character is received
// Load a value into Baud Rate Generator.
U4BRG = BRGVAL_RS485; // 60Mhz osc, 9600 Baud
// Load all values in for U1STA SFR
U4STAbits.UTXISEL1 = 0; // Bit15 Int when Char is transferred (1/2 config!)
U4STAbits.UTXISEL0 = 0; // Bit13 Other half of Bit15
U4STAbits.UTXINV = 1; // 1:UxTX Idle state is '0' ; 0=UxTX Idle state is '1';
U4STAbits.UTXBRK = 0; // Bit11 Disabled
U4STAbits.UTXEN = 0; // Bit10 TX pins controlled by peripheral
U4STAbits.URXISEL = 0; // Bits6,7 Int. on character received
IPC22bits.U4RXIP = 7;
IPC22bits.U4TXIP = 7;
IFS5bits.U4TXIF = 0; // Clear the Transmit Interrupt Flag
IEC5bits.U4TXIE = 0; // Enable Transmit Interrupts
IFS5bits.U4RXIF = 0; // Clear the Receive Interrupt Flag
IEC5bits.U4RXIE = 0; // Enable Receive Interrupts
RPOR2bits.RP39R = 0x1D; // dsPic33EP512GM604 => RP39 as U4TX PIN<43>
_U4RXR = 38; // dsPic33EP512GM604 => RP38 as U4RX PIN<42>
U4MODEbits.UARTEN = 1; // And turn the peripheral on
U4STAbits.UTXEN = 1;
// Hardware control bits
IEC5bits.U4RXIE = 1;
IFS5bits.U4RXIF = 0;
RS485_CTRL = 0; // disable driver; Receive Enable
}
In above code, I have a UART Receive Interrupt routine.
Whenever any character is received, UART ISR receives it, but unable to transmit anything back.
In my ISR, I am trying to send back the character received.
This issue might be related to the max485 control pins (!RE/DE), which is referred as RS485_CTRL in my code.
So, I tried to rectify the issue as below :
If ISR is written as
rs485Char = U4RXREG;
RS485_CTRL = 1; // Enable driver
U4TXREG = rs485Char;
then microcontroller transmit 2Bytes, first one is the character recieved, second byte is a false byte ie.0x00.Thereafter no character is received by the ISR.
If ISR is written as :
rs485Char = U4RXREG;
RS485_CTRL = 0; // Disable driver
U4TXREG = rs485Char;
RS485_CTRL = 1; // Enable driver
than it transmit a first character recieved. But there after ISR gets into infinite loop ie. receives a NULL character and transmits a NULL character.
As per the RS485 implementation rules,
RS485_CTRL (!RE/DE) should be 0 to receive data
RS485_CTRL (!RE/DE) should be 1 to transmit data.
My microcontroller is acting as a SLAVE, so by default I have kept it into listening mode. But when data is received I am unable to transmit.
Pls help me get my mistake identified ???
As per the suggestion given by #linuxfan, Correct ISR should be as below :
// UART ISR
void __attribute__((interrupt, no_auto_psv)) _U4RXInterrupt(void)
{
rs485Char = U4RXREG;
RS485_CTRL = 1; // Enable driver
U4TXREG = rs485Char;
while(!U4STAbits.TRMT); // wait until character is transffered successfully
RS485_CTRL = 0; // disable driver RE/DO
}
Now my code is working fine as expected.
The LTE-485 (RS-485 line driver) has a "driver enable pin" which must be asserted in order to transmit. You must assert it just before starting to send a character, and you must de-assert it after the last byte has finished transmission (you de-assert it in order to be able to receive data, or to let some other device to drive the bus. If you don't want to receive, and you don't have any other device willing to transmit, you can keep it asserted).
In your code there is:
RS485_CTRL = 1; // Enable driver
U4TXREG = rs485Char;
You enable the driver with RS485_CTRL=1, then you load the UART shift register, but perhaps you don't wait the time all the data is shifted out.
In order to wait for the data to shift out, you can load a timer which will fire after a fixed time: the time a character, 10 bits probably, takes to be shifted out at the baud rate you programmed. Each time you transmit a character, start this timer. When the timer expires, disable the OE (output driver enable).
Another way is to wait for the reception of the character you just sent out. You load the transmit register; when all the bits are shifted out, you will receive the character (RS-485 is a bus). At that time, you can disable the transmitter if you want. Of course, this is not very good if you want to transmit a stream of characters back-to-back. Moreover, in your hardware setup you can't do that because you disable the receiver (the receiver enable and driver enable are shorted together).
But you can do what I think is the best. When you transmit a char, enable the driver of the interface OE; then use the TX interrupt of your UART to transmit the next character (if any), or de-assert the driver if there is nothing more to transmit. Beware to use the right transmitting interrupt: some UART (I don't know the one you are using) has buffering - you need the interrupt that fires when the datum has shifted out, not the one which says "you can load more data".
NOTE ASIDE: you are using a DB9 connector, using the pins 2 and 3, and labelling them TX and RX. Well, there is NO TX and RX wires in the RS-485: the two wires are named A and B, and both carry the same, balanced signal. These wires are used to receive data, when left floating, and the same wires are used to transmit data, if driven by the LTC-485 driver. You can very well enable the transmitting driver and keep to "listen" on the wires. You will receive back what you just transmitted (and this can also diagnose that the wires are shorted or the driver is blown). You can choose to use a connector often used for RS-232, and you can also choose to use the same pins 2 and 3 as the RS-232, but in no way an RS-485 can be assimilated to an RS-232.
Related
I am a novice to Arduino. I came across an article on rs 485 and currently trying to implement the same between 2 arduino unos. I copied code from internet which is pretty simple but I am unable to make communication work properly. Here is the code for master arduino with explanation of what I am trying to do.
**Master Arduino Code**
/* master will send a message I1LF where I shows start of master message by master.
* 1 shows the slave device No. from whom data is to be fetched and L
* shows that master wants to read slave's sensor data
* and F indicates finish of message by master.
* slave will respond by sending Serial.println(i1) where i is slave no and 1 is to let master know that
* correct slave is sending data. then slave would send sensor_value and then f indicates end of slave message.
* master will display data read from rs 485 bus.
*/
#include<LiquidCrystal.h> //Library for LCD display
//function
LiquidCrystal lcd(4, 5, 6, 7, 8, 9);
const int Enable = 2;
int value1=0;
void setup()
{
Serial.begin(9600);
pinMode(Enable, OUTPUT);
digitalWrite(Enable, HIGH); // put master arduino into transmission mode.
lcd.begin(16,2);
lcd.clear();
}
void loop()
{
Serial.print('I'); // I indicates master has started communication.
Serial.print('1'); // this is the address of slave from whom data is to be fetched.
Serial.print('L'); // this means master wants to read sensor data.
Serial.print('F'); // this means end of message.
Serial.flush(); // wiat untill all data has been pushed to the serial.
digitalWrite(Enable, LOW); // put master into receiving mode.
if(Serial.find('i')) // i will be sent (to indicate start of slave
//message , by the slave that was prompted by
//master.
{
int slave = Serial.parseInt(); // check which slave is sending
//data.
value1 = Serial.parseInt();
if(Serial.read() == 'f' && slave == 1) // f indicates end of message
//sent by slave and 1 is the
//address of the slave sent by
// the slave so that master can
//recognize which slave is this.
{
lcd.setCursor(0,0);
lcd.print("Slave : ");
lcd.setCursor(11,0);
lcd.print(value1);
}
}
digitalWrite(Enable, HIGH);
delay(300);
}
This is the slave arduino code
#include<LiquidCrystal.h>
LiquidCrystal lcd(4, 5, 6, 7, 8, 9);
const int Enable = 2;
const int SlaveNumber = 1;
void setup()
{
Serial.begin(9600);
pinMode(Enable, OUTPUT);
digitalWrite(Enable, LOW); // initially put slave in receiving mode
pinMode(A0, INPUT);
lcd.begin(16,2);
lcd.clear();
}
void loop()
{
if(Serial.available())
{
if(Serial.read()=='I') // all slaves will detect I on rs 485 bus which //means master has started commmunication.
{
int Slave = Serial.parseInt(); // read next character on rs 485 bus
//which would definitely be a digit
//indicating slave no to whom master
//wishes to communicate.
if(Slave == SlaveNumber) // check if master wants to commnucate
//with you.
{
char command = Serial.read(); // read next character in
//serial buffer which would be
//command L
delay(100);
if(command == 'L')
{
if(Serial.read()=='F') // if master's message has
//finished.
{
int AnalogValue = analogRead(0); // read sensor
digitalWrite(Enable, HIGH); // put slave in
//transmission mode.
Serial.print("i"); // i indicates start of
//slave message
Serial.println(SlaveNumber); // send slave no so that
//master can identify which
//slave he is receiving
//data from.
Serial.print(AnalogValue);
Serial.print("f"); // indicates end of message by the
//slave.
Serial.flush();
digitalWrite(Enable, LOW); // put slave in
//listening ( receivig ) mode
}
}
}
}
}
}
This is screen shot of proteous simulation showing Master unable to read slave1's sensor data
enter image description here
I assessed that problem arose because of master and slave both being in transmission mode so I attached a not gate between master's enable pin and slave's enable pin and then master is working fine. but practically attaching a not gate wont be possible. that's why I want to know how to ensure that enable of both master and slave is not high simultaneously. I have tried a couple of strategies and introduced delays at different locations in the code but nothing seems to work. by hit and trial it starts to work sometimes but I want to know the correct way of doing so.
This is screen shot of proteous simulation which shows master reading correctly slave1 data only after I attach a not gate between master Enable pin and Slave's Enable pin.
Code for both master and slave and circuit is exactly the same as before. only a not gate has been used between master's enable pin and slave's enable pin.
enter image description here
I tried another library madleech/Auto485 but that i also showing the same problem. have tried a couple of times adding delays at different locations in master code but to no use. i know problem is because of enable pins of both MAX 485 modules connected to 2 arduinos going high simultaneously but i am unable to figure out how to address this issue.
You need to wait for each byte to be available before reading the serial port.
In your code:
if(Serial.available()) // you wait
{
if(Serial.read()=='I') // all slaves will detect I on rs 485 bus which //means master has started commmunication.
{
// parseInt() does wait for a byte to be availeble, so that works...
int Slave = Serial.parseInt();bus
if(Slave == SlaveNumber)
{
// Here serial.read() will most likely return -1, since it is very
// unlikely that a byte is already available at this point.
char command = Serial.read();
// ...
You should wait for the serial port to have received bytes before reading them.
You can try inserting a loop before calling read(), as in:
while (!Serial.available()) {} // wait for something to read
char byteRead = Serial.read();
This blocking code should help fix the code as you have it now. You should probably explore non-blocking ways of reading serial commands, such as using some sort of finite state machine algorithm. As a rule of thumb, embedded software should avoid blocking.
In this code i am taking the TID data(20bytes of 160bits) in the form of array according to the sparkfun(manufacture) documentation and RFID tag detection code and its working correctly and getting the output of RFID tags.
Now I just need your guidance that how can i stop displaying RFID tag ID which is already displayed on serial monitor of arduino. What should i have to do!
Arduino code:
/*Reading multiple RFID tags, simultaneously!
TIDs are 20 bytes, 160 bits*/
#include <SoftwareSerial.h> //Used for transmitting
SoftwareSerial softSerial(2, 3); //RX, TX
#include "SparkFun_UHF_RFID_Reader.h" //Library for controlling the M6E Nano module
RFID nano; //Create instance
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println();
Serial.println("Initializing...");
if (setupNano(38400) == false) //Configure nano to run at 38400bps
{
Serial.println("Module failed to respond. Please check wiring.");
while (1); //Freeze!
}
nano.setRegion(REGION_NORTHAMERICA); //Set to North America
nano.setReadPower(1000); //10.00 dBm.
nano.enableDebugging(); //Turns on commands sent to and heard from RFID module
}
void loop()
{
/*Serial.println(F("Get one tag near the reader. Press a key to read unique tag ID."));
while (!Serial.available()); //Wait for user to send a character*/
Serial.read(); //Throw away the user's character
byte response;
byte myTID[20]; //TIDs are 20 bytes
byte tidLength = sizeof(myTID);
//Read unique ID of tag
response = nano.readTID(myTID, tidLength);
if (response == RESPONSE_SUCCESS)
{
Serial.println("TID read!");
Serial.print("TID: [");
for(byte x = 0 ; x < tidLength ; x++)
{
if(myTID[x] < 0x10) Serial.print("0");
Serial.print(myTID[x], HEX);
Serial.print(" ");
}
Serial.println("]");
}
else
Serial.println("Failed read");
}
//Gracefully handles a reader that is already configured and already reading continuously
//Because Stream does not have a .begin() we have to do this outside the library
boolean setupNano(long baudRate)
{
nano.begin(softSerial); //Tell the library to communicate over software serial port
softSerial.begin(baudRate); //For this test, assume module is already at our desired baud rate
while(!softSerial); //Wait for port to open
while(softSerial.available()) softSerial.read();
nano.getVersion();
if (nano.msg[0] == ERROR_WRONG_OPCODE_RESPONSE)
{
//This happens if the baud rate is correct but the module is doing a ccontinuous read
nano.stopReading();
Serial.println(F("Module continuously reading. Asking it to stop..."));
delay(1500);
}
else
{
//The module did not respond so assume it's just been powered on and communicating at 115200bps
softSerial.begin(115200); //Start software serial at 115200
nano.setBaud(baudRate); //Tell the module to go to the chosen baud rate. Ignore the response msg
softSerial.begin(baudRate); //Start the software serial port, this time at user's chosen baud rate
}
//Test the connection
nano.getVersion();
if (nano.msg[0] != ALL_GOOD) return (false); //Something is not right
//The M6E has these settings no matter what
nano.setTagProtocol(); //Set protocol to GEN2
nano.setAntennaPort(); //Set TX/RX antenna ports to 1
return (true); //We are ready to rock
}
That is what you should do in your application program.
The basic specifications of the RFID reader and RFID tag are to notify all tags within the reading range when reading is requested.
Please incorporate the following procedures / functions into the application.
The tag data notified from the RFID reader is stored for each reading.
By the next reading, the tag data notified this time is compared with previously notified data.
Tag data that has already been received is not displayed.
Please decide the interval and the number of times to keep record of tag data and compare it according to your specifications and requirements.
In the case of tags conforming to ISO/IEC 18000-63, duplication notification can not be made within a certain period by specifying a parameter called S flag at the time of reading request.
However, since the detailed behavior depends on the specifications of individual tags and the operating environment, use of the S flag is not recommended.
I need to use a sparkfun breakout board ADXL345 to detect when my motor system has stopped vibrating. I am also using a Sparkfun RedBoard (Arduino uno).
Things I am doing to configure for this behavior:
enable INACTIVITY event
route INACTIVITY events to INT 1 (pin 2 on the RedBoard)
raise INACTIVITY interrupt without delay
set low threshold for INACTIVITY (rule out too high of a setting)
INACTIVITY considers all axes
clear interrupt data register
Having done all these things I do not receive interrupts after going from shaking the devise to setting it down.
//Add the SPI library so we can communicate with the ADXL345 sensor
#include <SPI.h>
//Assign the Chip Select signal to pin 10.
int CS=10;
//This is a list of some of the registers available on the ADXL345.
//To learn more about these and the rest of the registers on the ADXL345, read the datasheet!
char POWER_CTL = 0x2D; //Power Control Register
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32; //X-Axis Data 0
char DATAX1 = 0x33; //X-Axis Data 1
char DATAY0 = 0x34; //Y-Axis Data 0
char DATAY1 = 0x35; //Y-Axis Data 1
char DATAZ0 = 0x36; //Z-Axis Data 0
char DATAZ1 = 0x37; //Z-Axis Data 1
char THRESH_ACT = 0x24; // Activity threshold
char THRESH_INACT = 0x38; // Inactivity threshold to 3g
char TIME_INACT = 0x26; // time before raising interrupt
char INT_ENABLE = 0x2E; // Enabling the interrupt lines
char INT_MAP = 0x2F;
char ACT_INACT_CTL = 0x27; // mask byte for controlling
char INT_SOURCE = 0x30;
//This buffer will hold values read from the ADXL345 registers.
char values[10];
//These variables will be used to hold the x,y and z axis accelerometer values.
int x,y,z;
void setup(){
//Initiate an SPI communication instance.
SPI.begin();
//Configure the SPI connection for the ADXL345.
SPI.setDataMode(SPI_MODE3);
//Create a serial connection to display the data on the terminal.
Serial.begin(9600);
//Set up the Chip Select pin to be an output from the Arduino.
pinMode(CS, OUTPUT);
//Before communication starts, the Chip Select pin needs to be set high.
digitalWrite(CS, HIGH);
// Create an interrupt that will trigger when inactivity is detected
attachInterrupt(0, interruptHandler, RISING);
//Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
writeRegister(DATA_FORMAT, 0x01);
//Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
writeRegister(POWER_CTL, 0x08); //Measurement mode
// Send the inactivity && activity to PIN 1
// 0xF7 && 0xEF
writeRegister(INT_MAP,0xF7 && 0xEF);
// Set the inactivity threshold to 3g (0x38)
// writeRegister(THRESH_INACT,0x38);
writeRegister(THRESH_INACT,1);
// Raise the inact interrupt immediately after going below threshold
writeRegister(TIME_INACT,0);
// Map INACT event (only) to PIN 1
writeRegister(ACT_INACT_CTL, 0x0F);
// Enab le inactivity to generate interrupts
writeRegister(INT_ENABLE, 0x08);
readRegister(INT_SOURCE, 1, values); // Clear the INT_SOURCE register
Serial.println("Waiting for interrupt!");
}
void interruptHandler(){
// readRegister(INT_SOURCE, 1, values); // Clear the INT_SOURCE register
Serial.println("something raise an interrupt!");
}
void loop(){
//Reading 6 bytes of data starting at register DATAX0 will retrieve the x,y and z acceleration values from the ADXL345.
//The results of the read operation will get stored to the values[] buffer.
readRegister(DATAX0, 6, values);
//The ADXL345 gives 10-bit acceleration values, but they are stored as bytes (8-bits). To get the full value, two bytes must be combined for each axis.
//The X value is stored in values[0] and values[1].
x = ((int)values[1]<<8)|(int)values[0];
//The Y value is stored in values[2] and values[3].
y = ((int)values[3]<<8)|(int)values[2];
//The Z value is stored in values[4] and values[5].
z = ((int)values[5]<<8)|(int)values[4];
//Print the results to the terminal.
Serial.print(x, DEC);
Serial.print(',');
Serial.print(y, DEC);
Serial.print(',');
Serial.println(z, DEC);
delay(500);
}
//This function will write a value to a register on the ADXL345.
//Parameters:
// char registerAddress - The register to write a value to
// char value - The value to be written to the specified register.
void writeRegister(char registerAddress, char value){
//Set Chip Select pin low to signal the beginning of an SPI packet.
digitalWrite(CS, LOW);
//Transfer the register address over SPI.
SPI.transfer(registerAddress);
//Transfer the desired register value over SPI.
SPI.transfer(value);
//Set the Chip Select pin high to signal the end of an SPI packet.
digitalWrite(CS, HIGH);
}
//This function will read a certain number of registers starting from a specified address and store their values in a buffer.
//Parameters:
// char registerAddress - The register addresse to start the read sequence from.
// int numBytes - The number of registers that should be read.
// char * values - A pointer to a buffer where the results of the operation should be stored.
void readRegister(char registerAddress, int numBytes, char * values){
//Since we're performing a read operation, the most significant bit of the register address should be set.
char address = 0x80 | registerAddress;
//If we're doing a multi-byte read, bit 6 needs to be set as well.
if(numBytes > 1)address = address | 0x40;
//Set the Chip select pin low to start an SPI packet.
digitalWrite(CS, LOW);
//Transfer the starting register address that needs to be read.
SPI.transfer(address);
//Continue to read registers until we've read the number specified, storing the results to the input buffer.
for(int i=0; i<numBytes; i++){
values[i] = SPI.transfer(0x00);
}
//Set the Chips Select pin high to end the SPI packet.
digitalWrite(CS, HIGH);
}
Here is a tutorial, arduino library and example sketch. If you haven't run through something like this, might be worth a try starting with someone else's code that is working (maybe you've already done that).
In the example sketch from above, they are enabling interrupts in the code, they just don't seem to tie them into the Arduino's external interrupt system. Once you verify that the example code is working, you can call attachInterrupt() and abandon the polling approach (as you are doing in your example).
I intend to read the data received by the xbee in an interrupt handler.
But as the handler can not use delays, I can not use readPacket (100).
I have the following code:
#include <XBee.h>
#include <avr/power.h>
#include <avr/sleep.h>
XBee xbee = XBee();
XBeeResponse response = XBeeResponse();
ZBRxResponse rx = ZBRxResponse();
int size;
uint8_t buffer[256];
void setup() {
Serial.begin(9600);
Serial1.begin(9600);
xbee.begin(Serial1);
attachInterrupt(0, wake_up_now, LOW );
}
void wake_up_now() {
xbee.readPacket();
if(xbee.getResponse().isAvailable()){
if (xbee.getResponse().getApiId() == ZB_RX_RESPONSE) {
xbee.getResponse().getZBRxResponse(rx);
size = rx.getDataLength();
for (int i = 0; i < size; i++)
buffer[i] = rx.getData(i);
}
}
}
void sleepNow() {
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_enable();
power_adc_disable();
power_spi_disable();
power_timer0_disable();
power_timer1_disable();
power_timer2_disable();
power_twi_disable();
sleep_mode();
sleep_disable();
power_all_enable();
}
void loop() {
Serial.print("Awake");
Serial.println(size);
for (int i = 0; i < size; i++)
Serial.println(buffer[i]);
Serial.println("Entering Sleep mode");
delay(100);
sleepNow();
}
If you receive data the first time I can not read this data.
But later, when more data is received the data that was sent in the first message is read.
I really need to read the data in the handler, how can I solve this?
When I designed this ANSI C XBee Host library, I thought I could include frame processing as part of the serial interrupt handler on the Freescale HCS08 platform. I found that it just didn't work well, especially at a baud rate of 115,200.
The final design I went with was to keep a buffer of bytes from the serial port, and parse/dispatch frames in a "tick" function called in my main event loop. In one application, I was calling the "tick" function at least once every 50ms (IIRC).
As for forcing the XBee module to buffer serial data while you're sleeping, I think you'll have a hard time with that. You'd need to deassert RTS, but then wait to see if the XBee was still sending you a byte. My recollection is that I would still get multiple bytes (3 or 4?) after deasserting RTS. You'll also have to handle the condition of getting a partial packet while you're awake, and needing to decide whether you'll receive the whole frame then, or the next time you wake up.
You'd probably be better off using the XBee module's SPI interface, since the host controls when bytes are sent, and it might even be possible to have the XBee trigger a hardware interrupt on the host when it has bytes to send. You can also use high speeds (460kbps and up, I believe) to get the bytes quickly.
I have one Arduino with 4 Pots. The other Arduino receives these 4 values via i2c and prints them on a Display. The problem is that I don't know how to send these 4 values that the Slave is able to know which value belongs to which Pot.
Slave Code:
#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup()
{
Wire.begin(5);
Wire.onReceive(receiveEvent);
Serial.begin(9600);
lcd.begin(16,2);
}
void loop()
{
}
void receiveEvent(int)
{
while(Wire.available())
{
//How to create this part? How does the Slave know which value belongs to which pot?
}
}
Master Code:
#include <Wire.h>
void setup()
{
Serial.begin(9600);
Wire.begin();
delay(2000);
}
void loop()
{
int sensor1 = analogRead(A1);
Wire.beginTransmission(5);
Wire.write(sensor1);
Serial.print(sensor1);
Wire.endTransmission();
delay(100);
int sensor2 = analogRead(A2);
Wire.beginTransmission(5);
Wire.write(sensor2);
Serial.print(sensor2);
Wire.endTransmission();
delay(500);
}
Ahh what we have here is a basic question on how to design I2C communication. Unfortunately Examples for I2C master and slave included in Arduino IDE are IMO too limited to provide clear guidance on this matter.
First of all in your examples the master and slaves roles are exchanged and should be switched. Slave should read values from analog inputs and master should request them. Why? Because it's master which should decide when to request values and properly decode the request. Slave should provide proper answer to a given request eliminating the problem of data interpretation.
I2C communication is based on requestFunction-(wait)-requestResponse sequence controlled by the master.
Plese refer to the range finder example on arduino page. In a nutshell:
First: master requests a function to measure distance:
// step 3: instruct sensor to return a particular echo reading
Wire.beginTransmission(112); // transmit to device #112
Wire.write(byte(0x02)); // sets register pointer to echo #1 register (0x02)
Wire.endTransmission(); // stop transmitting
(sometimes slaves need some time e.g. 10 - 50 ms to process requests but in the example I'm refering to master doesn't delay read)
Second: master requests response:
// step 4: request reading from sensor
Wire.requestFrom(112, 2); // request 2 bytes from slave device #112
Third: master tries to read and analyze response.
You should design reliable I2C communication in a similar way.
Here is how I do it; you can follow my pattern and get extensible slave implementation which will support one function: read analog inputs but can be easily extended by adding additional function codes and required processing implementation to the slave main loop
Initial remarks
some kind of a simple protocol is needed to control slave - e.g. it should support requesting functions. Supporting functions requests is not absolutely needed in such simmple scenario as reading four analog inputs but what I'm describing is a more general pattern you may use in other projects.
Slave should not perform any additional actions (like reading inputs) on request response as I2C communication may break (due to delays) and you will get partial responses etc. This is very important requirement which affect the slave design.
response (and also request if needed) can contain CRC as if master waits not long enough it may get empty response. If nobody else is going to use your code such countermeasures are not needed and will not be described here. Other important thing is Wire library buffer limitation which is 32 bytes and implementing CRC checksum without modifying the buffer length limits the available data length by two bytes (if crc16 is used).
slave:
#include <WSWire.h> // look on the web for an improved wire library which improves reliability by performing re-init on lockups
// >> put this into a header file you include at the beginning for better clarity
enum {
I2C_CMD_GET_ANALOGS = 1
};
enum {
I2C_MSG_ARGS_MAX = 32,
I2C_RESP_LEN_MAX = 32
};
#define I2C_ADDR 0
#define TWI_FREQ_SETTING 400000L // 400KHz for I2C
#define CPU_FREQ 16000000L // 16MHz
extern const byte supportedI2Ccmd[] = {
1
};
// << put this into a header file you include at the beginning for better clarity
int argsCnt = 0; // how many arguments were passed with given command
int requestedCmd = 0; // which command was requested (if any)
byte i2cArgs[I2C_MSG_ARGS_MAX]; // array to store args received from master
int i2cArgsLen = 0; // how many args passed by master to given command
uint8_t i2cResponse[I2C_RESP_LEN_MAX]; // array to store response
int i2cResponseLen = 0; // response length
void setup()
{
// >> starting i2c
TWBR = ((CPU_FREQ / TWI_FREQ_SETTING) - 16) / 2;
Wire.begin(I2C_ADDR); // join i2c bus
Wire.onRequest(requestEvent); // register event
Wire.onReceive(receiveEvent);
// << starting i2c
}
void loop()
{
if(requestedCmd == I2C_CMD_GET_ANALOGS){
// read inputs and save to response array; example (not tested) below
i2cResponseLen = 0;
// analog readings should be averaged and not read one-by-one to reduce noise which is not done in this example
i2cResponseLen++;
i2cResponse[i2cResponseLen -1] = analogRead(A0);
i2cResponseLen++;
i2cResponse[i2cResponseLen -1] = analogRead(A1);
i2cResponseLen++;
i2cResponse[i2cResponseLen -1] = analogRead(A2);
i2cResponseLen++;
i2cResponse[i2cResponseLen -1] = analogRead(A3);
// now slave is ready to send back four bytes each holding analog reading from a specific analog input; you can improve robustness of the protocol by including e.g. crc16 at the end or instead of returning just 4 bytes return 8 where odd bytes indicate analog input indexes and even bytes their values; change master implementation accordingly
requestedCmd = 0; // set requestd cmd to 0 disabling processing in next loop
}
else if (requestedCmd != 0){
// log the requested function is unsupported (e.g. by writing to serial port or soft serial
requestedCmd = 0; // set requestd cmd to 0 disabling processing in next loop
}
}
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent(){
Wire.write(i2cResponse, i2cResponseLen);
}
// function that executes when master sends data (begin-end transmission)
// this function is registered as an event, see setup()
void receiveEvent(int howMany)
{
//digitalWrite(13,HIGH);
int cmdRcvd = -1;
int argIndex = -1;
argsCnt = 0;
if (Wire.available()){
cmdRcvd = Wire.read(); // receive first byte - command assumed
while(Wire.available()){ // receive rest of tramsmission from master assuming arguments to the command
if (argIndex < I2C_MSG_ARGS_MAX){
argIndex++;
i2cArgs[argIndex] = Wire.read();
}
else{
; // implement logging error: "too many arguments"
}
argsCnt = argIndex+1;
}
}
else{
// implement logging error: "empty request"
return;
}
// validating command is supported by slave
int fcnt = -1;
for (int i = 0; i < sizeof(supportedI2Ccmd); i++) {
if (supportedI2Ccmd[i] == cmdRcvd) {
fcnt = i;
}
}
if (fcnt<0){
// implement logging error: "command not supported"
return;
}
requestedCmd = cmdRcvd;
// now main loop code should pick up a command to execute and prepare required response when master waits before requesting response
}
master:
#include <WSWire.h>
#define I2C_REQ_DELAY_MS 2 // used for IO reads - from node's memory (fast)
#define I2C_REQ_LONG_DELAY_MS 5 //used for configuration etc.
#define TWI_FREQ_SETTING 400000L
#define CPU_FREQ 16000000L
enum {
I2C_CMD_GET_ANALOGS = 1
};
int i2cSlaveAddr = 0;
void setup(){
// joining i2c as a master
TWBR = ((CPU_FREQ / TWI_FREQ_SETTING) - 16) / 2;
Wire.begin();
}
void loop(){
//requesting analogs read:
Wire.beginTransmission(i2cSlaveAddr);
Wire.write((uint8_t)I2C_CMD_GET_ANALOGS);
Wire.endTransmission();
delay(I2C_REQ_DELAY_MS);
// master knows slave should return 4 bytes to the I2C_CMD_GET_ANALOGS command
int respVals[4];
Wire.requestFrom(i2cSlaveAddr, 4);
uint8_t respIoIndex = 0;
if(Wire.available())
for (byte r = 0; r < 4; r++)
if(Wire.available()){
respVals[respIoIndex] = (uint8_t)Wire.read();
respIoIndex++;
}
else{
// log or handle error: "missing read"; if you are not going to do so use r index instead of respIoIndex and delete respoIoIndex from this for loop
break;
}
// now the respVals array should contain analog values for each analog input in the same order as defined in slave (respVals[0] - A0, respVals[1] - A1 ...)
}
I hope my example will help. It's based on code working for weeks making 40 reads a second from multiple slaves however I have not compiled it to test the function you require.
Please use WSWire library as the Wire (at least as for Arduino 1.0.3) may occasionally freeze your master if for some reason slave will not respond to request.
EDIT: The WSWire lib requires external pull-up resistors for I2C unless you modify the source and enable internal pull-ups like Wire does.
EDIT: instead of creating i2c slave implementation you may try the EasyTransfer library. I haven't tried it but it may be easier to use it if sending four bytes is everything you need.
EDIT[12.2017]: There is a new player on the block - PJON - a library suited for easy multi-master communication ideal to exchange pot values (and much more). It's been around for some time but gained a substantial development speed in recent months. I'm partially involved in its development and switched all field-level and local buses I've used so far (I2C, MODBUS RTU) to PJON over single wire, hardware serial or RF.
Check out GitHub-I2CBus, I've done the exact same thing. Hope it can help