I'm trying to talk serial with an SDI-12 device, and it requires inverted seven data bits, even parity and one stop bit (7E1) serial at 1200 baud.
From the datasheet:
SDI-12 communication sends characters at 1200 bits per second. Each character has 1 start bit, 7 data bits (LSB first), 1 even parity bit, and 1 stop bit (Active low or inverted logic levels):
All SDI-12 commands and response must adhere to the following format on the data line. Both the command and response are preceded by an address and terminated by a carriage return line feed combination.
Is this possible with the Serial or SoftwareSerial libraries? I am trying to avoid additional hardware (beyond a levelshifter to 3.3 V), but I will do so if it is the only way.
I have seen that SoftwareSerial can do inverted, and Serial can do 7E1, but I can't find if either can do both.
I have access to a Arduino Mega (R2), and Arduino Uno (R3).
Here is the device I want to communicate with: http://www.decagon.com/products/sensors/soil-moisture-sensors/gs3-soil-moisture-temperature-and-ec/ and here, http://www.decagon.com/assets/Uploads/GS3-Integrators-Guide.pdf is the document explaining the protocol. Page 6 talks about its implementation of SDI.
I'm not familiar with Arduino, however the SDI-12 physical layer is inverted from the standard TTL levels - probably for two reasons:
Since the idle voltage is 0V, this results in lower standby power (due to nominal pull-down resistors in a typical SDI-12 sensor.
It facilitates simple bus 'sniffing' using a standard RS-232 serial port.
Short of bit-banging a 5V IO pin - yes, if using a standard microcontroller UART you will need an external inverter (or 2) and a 3-state buffer. Possibly requiring level shifting, depending on your hardware.
Thumbs down to the Wikipedia entry - SDI-12 uses entirely standard UART bit timings (very much like RS-232), just different signal levels (0 - 5V); see point #2. However, there are specific break sequences and strict timing requirements, which makes firmware development more difficult.
If you are serious about SDI-12 firmware development, you may want to invest in an SDI-12 Verifier. A thorough study of the specification is essential.
A little late... but better late than never
I have actually just written a library for exactly that (actually exactly that including the sensors ... so it should work exactly with the included examples )
https://github.com/joranbeasley/SDISerial (Arduino Library)
#include <SDISerial.h> //https://github.com/joranbeasley/SDISerial (Arduino Library)
#include <string.h>
#define DATA_PIN 2
SDISerial connection(DATA_PIN);
char output_buffer[125]; // just for uart prints
char tmp_buffer[4];
char sensor_info[]
//initialize variables
void setup(){
connection.begin();
Serial.begin(9600);//so we can print to standard uart
//small delay to let the sensor do its startup stuff
delay(3000);//3 seconds should be more than enough
char* sensor_info = connection.sdi_query("0I!",1000); // get sensor info for address 0
}
//main loop
void loop(){
//print to uart
Serial.println("Begin Command: ?M!");
//send measurement query (M) to the first device on our bus
char* resp = connection.service_request("0M!","0D0!");//Get Measurement from address 0
sprintf(output_buffer,"RECV: %s",resp?resp:"No Response Recieved!!");
Serial.println(output_buffer);
delay(10000);//sleep for 10 seconds before the next read
}
Related
I am starting to study the Wire library (no previous Arduino Wire library experience), I read some info taken from here.
As you all know, this really simple example changes the value of a AD5171 digital potentiometer via I2C. Written by Nicholas Zambetti and Shawn Bonkowski, demonstrates use of the Wire library.
I just copied and reduced the code below a little from the example. I am an experienced assembler and C/C++ programmer and hardware developer/designer. Although several I2C devices like DS3231 RTC, etc. work fine using standard Arduino libraries, the mentioned example doesn't work for me in my working NANO board. What am I doing wrong?
This code should transmit:
first the I2C protocol device address - Start / 8 + 1bits
test instruction data byte
variable 1 test byte constantly incremented
I2C Stop condition
The only byte I can see in my oscilloscope is just the first one (please see picture below). The 2 data bytes are not being transmitted. If I reduce the transmission to just the step #2 instruction single data byte, the same result is shown.
#include <Wire.h>
void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
}
byte val = 0;
void loop()
{
Wire.beginTransmission(44); // transmit to device #44 (0x2c)
Wire.write(byte(0x55)); // sends instruction byte
Wire.write(val++); // sends potentiometer value byte
Wire.endTransmission(); // stop transmitting
delay(50); // some time delay for my oscilloscope
}
This is what this code produces:
I see transferred data as 01011000 1. I hope the is right decoded. Why you not zoom it more for better reading? It is hard see data levels on raising clock edges. This match to address 44, 0x2C . As you can also see that you get NACK on the end of address octet. So this means no device with transmitting address is on the bus.
You can get error code about sending process from endTransmission function as return int. Send this value to serial monitor.
Error codes you can see in arduino documentation
uint8_t err;
err= Wire.endTransmission(); // stop transmitting and get status
You can also use sketch i2c_scanner from arduino Wire examples to discovery right address of your device.
This is driving me nuts for a couple of days now, so maybe you guys can give me some insights into what is going wrong.
I'm trying to read some data from an EEPROM (24LC16B) with an STM32(F0), but it just doesn't let me. I've tried an Arduino, which worked and does still work, so I do know that the wiring is correct.
This is my function to read the EEPROM data. (It is cut down to the very basis, just for testing): (Pastebin of my I2C_setup function)
uint16_t readEEPROMData(uint16_t deviceAddress, int memAddress){
// Wait while I2C peripheral is not ready
I2C_WaitForFlag(I2C_ISR_BUSY);
// Start I2C write transfer for 2 bytes, do not end transfer (SoftEnd_Mode)
I2C_TransferHandling(I2C1, 0xA2, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
I2C_WaitForFlag(I2C_ISR_TXIS);
// For testing purpose, be sure to generate a stop command...
I2C_TransferHandling(I2C1, 0xA2, 0, I2C_AutoEnd_Mode, I2C_Generate_Stop);
return I2C_COMM_STATUS;
}
Here's an pastebin of the Arduino library I used.
I've used a logic analyzer to see how the communication is going, and now I really don't understand it. Here's a printscreen of the working Arduino version:
And here's a printscreen of the STM32 communication:
Logic analyzer exports (viewable with Saleae Logic)
As you can see, I'm using the same address (although I had to use 0xA2 with the STM32), and there are no weird things happening, besides the NACK. So what could possible be wrong?
Confirm if all bus timing requirement are satisfied.
Confirm if their is adequate delay after every write cycle (5 mS)
Confirm is bus capacitance falls under permissible limit of I2C (400 pF - Theoretically).
Confirm if the correct VCC is supplied
As mentioned by you are interfacing EEPROM with MCU using cable you need to conform on capacitance.
You can use an oscilloscope to check if their are any distortion in waveform. You can use a LCR meter to check the capacitance.
Try reducing bus speed 25kHz to 50 kHz and check waveform.
Try increasing the strength of pull resister.
The problem with the wrong VCC capacity (4.2v instead of 5v for example) is, that the timing can be different to. (not fully verified, but it fixed the problem)
I'm working through a set of beginner exercises with the Arduino Uno microcontroller. (A generic one, though, as this is what I've been supplied with.)
The program I'm running, which alternates between sending 1's and 0's to serial output depending on the state of a momentary switch, has set pin 2 to be the input for the switch. But. Whilst wiring up, I accidentally plugged the jumper cable in to pin 3 initially, and found it still mostly sent the 1's when the button was pushed. Some 0's, yet mostly 1's.
Initially I thought maybe it was just the board was a bit dodgy, but thought I'd experiment a bit. Plugging into pin 3 instead of pin 2 still fairly consistently sent 1's when the button was pushed, though the 1's flowed a little bit less consistently than when it was in pin 2. In pin 2 it was completely consistent by comparison. So I tried pin 4, but with that one there's no response at all.
Am I right in presuming the program's readings seems to get a little bit less responsive the further away I move the cable from the pin that I've programmed to act as input? Can anyone help me understand why this happens?
It's probably quite obvious that I'm new to electronics. :)
The program I've got uploaded to the board is as follows:
// digital pin 2 has a pushbutton attached to it. Give it a name:
int pushButton = 2;
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
// make the pushbutton's pin an input:
pinMode(pushButton, INPUT);
}
// the loop routine runs over and over again forever:
void loop() {
// read the input pin:
int buttonState = digitalRead(pushButton);
// print out the state of the button:
Serial.println(buttonState);
delay(1); // delay in between reads for stability
}
Floating pins are prone to noise. If you are not actually connecting anything to pin 2, you will be reading noise. Any wire connected to pin 2 (even connections on the board) will act like an antenna and pick up noise. You should always use the pin number that you are physically connecting in situations like these.
Leaving input pins open makes the microcontroller read a floating value which swings between 0 to 1. Also when wiring a switch to any pin, make sure to hook some pull-down resistor to make the input 0.
These are common for many electronics and proper notice to be taken while designing circuits of your own.
You need to look into datasheet where described functions of the pins.
The pins of MCU can be assigned various functions through special registers.
Two most common functions of the pins are input and output. MCUs provide internal pull-up and pull-down resistors which when used properly significantly simplifies electronic schemas.
If the input activated as input without any pull-??? then it's state is not defined and can be used as initiator of random number generator. Due this reason it is better to define what is default state of the input pin by connecting pull-??? resistors.
In Arduino IDE you are not limited to functions provided -- you still can use register manipulation directly, you just need to learn internals of the MCU.
If you do it properly then 2kbit program very often can be made as small as a few hundred bytes and it will work hundred times faster.
Operating registers in C is not much different from assembly, in C++ you get right away significant overhead -- although some benefits of registers still can be significant.
Libraries hide from programmers internals of MCU what is nice as it simplifies the programming and does not require to understand how MCU works, what registers are changed in what sequence.
But when you know hardware in and out -- you can squeeze from small MCU what is not possible with use of libraries (code will just not fit into the chip). MCUs are not that complicated (Atmel) to learn about it's internals -- benefits are significant.
Knowledge is a power which many avoid.
I'm trying to do something really simple and I'm having a bit of trouble getting it to work. I'm working with the MPL3115A2 Altitude/Pressure Sensor and a pic32 uC32 board, and I'm trying to communicate between the two using I2C. (uC32 board is similar enough to arduino that it's practically the same in terms of coding).
I'm using the wire library and I'm simply trying to read register 0x0C from the MPL3115A2, which should give me the device ID.
Here's a code snippet (the define is at the top of the code and the rest is in the main loop):
#define barAddress 0x60
Wire.beginTransmission(barAddress);
Wire.send(0x0C);
Wire.endTransmission();
Wire.requestFrom(barAddress, 1);
uint8_t a = Wire.receive();
Serial.println(a, HEX);
So I start the transmission with address 0x60 (From the datasheet: The standard 7-bit I2C slave address is 0x60 or 1100000. 8-bit read is 0xC1, 8-bit write is 0xC0.). Then I send 0x0C because that's the register I want to access. I then end transmission, and request 1 byte from address 0x60, receive that bit into a 8-bit variable, then print it.
The problem I run into is that when I print it, I just get 0. I don't get the device ID, just 0. No matter what register I try to read, I get 0.
I've been banging my head against a wall for the past few days trying to get this to work. I've attached something I've captured with a logic analyzer, as well as a list of registers from the datasheet of the MPL3115A2 that I've been trying to access.
Using a logic analyzer I can see the clock and data lines. The clock seems normal and the data line gives me the following:
START
Write['192'] + ACK
'12' + ACK
STOP
START
Read['193'] + ACK
'0' + NAK
STOP
This all seems correct to me (192 and 193 come from 8-bit write and read being 0xC0 and 0xC1), except for the '0'. I should be getting the device ID, not 0.
Thanks for any help with this!
You should look at Freescale's app note AN4481, which is referred to by the datasheet. Page 5 shows the single-byte read operation which is what you are doing, except that the register address write must not be followed by a STOP but instead uses a REPEATED-START.
I'm not familiar with the Wire library but it look like all you need to do is remove the Wire.endTransmission(); between the send and requestFrom.
Hopefully, this will solve your problem.
I am trying to read the data from a Ultra Sonic Fuel Sensor(the link).The Baud rate of this device is 9600.The device basically sends data at regular time intervals.I am able to read the output in the PC using Terminal software.Given below is a sample.
Eg:*XD,205B,00,0000,0031,0000,0000,null#
I am trying to connect this device to Arduino through serial port provided in the device and when I see the Serial Monitor,the output is not correct.Given below is the sample.
5320215115451166102572432302302432302302302302432303816623024323023023023024323023023023051822281141463
String incoming_char; // Will hold the incoming character from the Serial Port.
void setup()
{
//Initialize serial ports for communication.
Serial.begin(9600);
Serial1.begin(9600);
Serial.println("Starting Communication with Fuel Sensor");
}
void loop()
{
//If a character comes in from the cellular module...
if(Serial1.available() >0)
{
incoming_char=String(Serial1.read()); // Get the character from the cellular serial port.
Serial.print(incoming_char); // Print the incoming character to the terminal.
}
}
The Arduino is powered from USB and the Device from a 12V supply.
The voltage levels from the device Tx-GND=-5.44V,Rx-GND=-8.22V.
I initially thought the the issue might be because of the voltage range and made a voltage divider circuit and fed Arduino the proportionate voltage.Even that is not working.
So,what is the thing which is going wrong ?Please guide me.
Since you are using Serial1 I am assuming you are using an Arduino Mega?
From your question I would say the issue isn't voltage etc. but more likely to be how you are reading the data. You are assuming that the sensor will be returning char values. Are there any specifications on what is being returned?
I created a similar project using an Arduino. Except my Ultrasonic device was used as a range finder. There are details here. As you can see in the code the range is returned from the sensor as a two byte integer.
You will need to find out what the what the Ultra Sonic Fuel Sensor is returning and read in a similar fashion.
allright i would start by suggesting that you connect this to an analog pin to read. you will have to find the ratio between the fuel hight and voltage by measuring and dividing. then insert the multiplication in the code and you are set it will look like the hight instead of just a voltage
This is just a wild guess since I don't own an Arduino Mega (I have Duemilanove and Uno), but I've worked on projects wherein I've encountered issues similar to what you have. Sometimes adding a delay() on your void loop() block helps and gives it enough time for the arduino to read the bytes from the buffer. For 9600 baud rate, it usually takes about 1 ms to read 1 byte so adding a delay is necessary.
void loop()
{
//If a character comes in from the cellular module...
if(Serial1.available() >0)
{
incoming_char=String(Serial1.read()); // Get the character from the cellular serial port.
Serial.print(incoming_char); // Print the incoming character to the terminal.
}
delay(100);
}
Thank you all ! for your Value inputs.The problem was, I was trying to connect RS232 Serial(Works with Negative Voltages) to TTL serial interface(Works with 0 to some Positive Voltages) used in Arduino. Apparently,I was supplying negative voltages to Arduino whereas it was expected to give Positive voltages. So, got a RS232-to-TTL connector and it worked,finally.