This article describes how implement a onewire slave device into an Arduino Nano
The simple implementation makes the Arduino Nano able to use i2c to communicate with the ultrasonic distance sensor SRF02, and present the result to the OWFS environment
The implemntation also includes two ports to control LED on the Arduino nano.
The project includes the following steps:
To accomplice this, I have made use of greate work from:
Change to source directory:
cd /usr/src
wget http://sourceforge.net/projects/owfs/files/owfs/3.1p0/owfs-3.1p0.tar.gz/download tar zxf downloadgoto top
Go into source directory:
cd owfs_3.1p0
For compilation, update and install requirements:
apt-get update apt-get upgrade -y
apt-get -y install automake autoconf autotools-dev gcc-4.7 libavahi-client-dev libtool libusb-dev apt-get -y install libusb-1.0-0-dev libfuse-dev swig python2.7-dev tcl8.5-dev php5-dev i2c-tools
Find the file libusb.h and copy to /usr/src/owfs_3.1p0/module/owlib/src/include.
At this point, make sure you can compile the original source
./configure make -j 4 make install
The executable modules are now in /opt/owfs
goto top
To accomodate a new OneWire device,
the following files needs to be created:
Create file in editor:
node /usr/src/owfs_3.1p0/module/owlib/src/c/ow_arduino.c
insert the following into the file:
/* OWFS -- One-Wire filesystem OWHTTPD -- One-Wire Web Server Written 2003 Paul H Alfille email: paul.alfille@gmail.com Released under the GPL See the header file: ow.h for full attribution 1wire/iButton system from Dallas Semiconductor */ /* General Device File format: This device file corresponds to a specific 1wire/iButton chip type ( or a closely related family of chips ) The connection to the larger program is through the "device" data structure, which must be declared in the acompanying header file. The device structure holds the family code, name, number of properties, list of property structures, called "filetype". Each filetype structure holds the name, estimated length (in bytes), aggregate structure pointer, data format, read function, write funtion, generic data pointer The aggregate structure, is present for properties that several members (e.g. pages of memory or entries in a temperature log. It holds: number of elements whether the members are lettered or numbered whether the elements are stored together and split, or separately and joined */ /* Changes 7/2004 Extensive improvements based on input from Serg Oskin */ #include#include "owfs_config.h" #include "ow_arduino.h" /* ------- Prototypes ----------- */ /* ARDUINO switch */ READ_FUNCTION(FS_r_ledstate); WRITE_FUNCTION(FS_w_ledstate); READ_FUNCTION(FS_r_pio); WRITE_FUNCTION(FS_w_pio); READ_FUNCTION(FS_r_dist); /* DS2423 counter */ //READ_FUNCTION(FS_r_memory); //WRITE_FUNCTION(FS_w_memory); //READ_FUNCTION(FS_r_status); //READ_FUNCTION(FS_r_application); //WRITE_FUNCTION(FS_w_application); #define _DS2430A_MEM_SIZE 4 /* ------- Structures ----------- */ static struct aggregate A2413 = { 2, ag_letters, ag_aggregate }; static struct filetype ARDUINO[] = { F_STANDARD, {"ledstate", PROPERTY_LENGTH_UNSIGNED, NON_AGGREGATE, ft_unsigned, fc_volatile, FS_r_ledstate, FS_w_ledstate, INVISIBLE, NO_FILETYPE_DATA, }, {"LED", PROPERTY_LENGTH_BITFIELD, &A2413, ft_bitfield, fc_link, FS_r_pio, FS_w_pio, VISIBLE, NO_FILETYPE_DATA, }, { "DIST", PROPERTY_LENGTH_UNSIGNED, NON_AGGREGATE, ft_unsigned, fc_link, FS_r_dist, NO_WRITE_FUNCTION, VISIBLE, NO_FILETYPE_DATA, }, }; DeviceEntryExtended(E2, ARDUINO, DEV_resume | DEV_ovdr, NO_GENERIC_READ, NO_GENERIC_WRITE); /* 2413 Commands */ #define _1W_PIO_ACCESS_READ 0xF5 #define _1W_PIO_ACCESS_WRITE 0x5A #define _1W_DIST_ACCESS_READ 0x55 #define _1W_2413_LATCH_MASK 0x0A #define _1W_2413_CONFIRMATION 0xAA #define _1W_COUNTER_FILL 0x00 /* Persistent storage */ Make_SlaveSpecificTag(CUM, fc_persistent); // cumulative /* ------- Functions ------------ */ /* ARDUINO */ //----------------------------------------------------------------- /* 2413 */ //----------------------------------------------------------------- static GOOD_OR_BAD OW_write(BYTE data, const struct parsedname *pn); static GOOD_OR_BAD OW_read(BYTE * data, const struct parsedname *pn); static GOOD_OR_BAD OW_readdist(BYTE * data, const struct parsedname *pn); static UINT SENSED_state(UINT status_bits); static UINT LATCH_state(UINT status_bits); static ZERO_OR_ERROR FS_r_ledstate(struct one_wire_query *owq) { /* surrogate property bit 0 PIOA pin state bit 1 PIOA latch state bit 2 PIOB pin state bit 3 PIOB latch state */ BYTE ledstate ; if ( OW_read( &ledstate, PN(owq) ) ) { return -EINVAL ; } OWQ_U(owq) = ledstate ; return 0 ; } static ZERO_OR_ERROR FS_r_diststate(struct one_wire_query *owq) { BYTE diststate; if (OW_readdist(&diststate, PN(owq))) { return -EINVAL; } OWQ_U(owq) = diststate; return 0; } static ZERO_OR_ERROR FS_w_ledstate(struct one_wire_query *owq) { UINT pio = OWQ_U(owq) ; return OW_write( pio, PN(owq) ) ? -EINVAL : 0 ; } /* bites 1 and 3 */ static ZERO_OR_ERROR FS_r_pio(struct one_wire_query *owq) { UINT ledstate = 0 ; ZERO_OR_ERROR z_or_e = FS_r_sibling_U( &ledstate, "ledstate", owq ) ; // bits 1>0 and 3->1 // complement OWQ_U(owq) = LATCH_state( ledstate ) ^ 0x03 ; return z_or_e ; } static ZERO_OR_ERROR FS_r_dist(struct one_wire_query *owq) { UINT diststate = 0; ZERO_OR_ERROR z_or_e = OW_readdist(&diststate, PN(owq)); OWQ_U(owq) = diststate; return z_or_e; } /* write ARDUINO switch -- 2 values*/ static ZERO_OR_ERROR FS_w_pio(struct one_wire_query *owq) { // complement ZERO_OR_ERROR ret = FS_w_sibling_U( OWQ_U(owq) ^ 0x03 , "ledstate", owq ); if ( ret == 0 ) { // Able to write, but ledstate is potentially now incorrect // since we use a different method to set pios FS_del_sibling( "ledstate", owq ) ; return 0 ; } return ret ; } static GOOD_OR_BAD OW_readdist(BYTE * data, const struct parsedname *pn) { BYTE cmd[] = { _1W_DIST_ACCESS_READ, }; BYTE resp[1]; struct transaction_log t[] = { TRXN_START, TRXN_WRITE1(cmd), TRXN_READ1(resp), TRXN_END, }; RETURN_BAD_IF_BAD(BUS_transaction(t, pn)); data[0] = resp[0]; return gbGOOD; } static GOOD_OR_BAD OW_read(BYTE * data, const struct parsedname *pn) { BYTE cmd[] = { _1W_PIO_ACCESS_READ, }; BYTE resp[1] ; struct transaction_log t[] = { TRXN_START, TRXN_WRITE1(cmd), TRXN_READ1(resp), TRXN_END, }; RETURN_BAD_IF_BAD(BUS_transaction(t, pn)) ; // High nibble the complement of low nibble? // Fix thanks to josef_heiler if ((resp[0] & 0x0F) != ((~resp[0] >> 4) & 0x0F)) { return gbBAD; } data[0] = resp[0] & 0x0F ; return gbGOOD; } /* write status byte */ /* top 6 bits are set to 1, complement then sent */ static GOOD_OR_BAD OW_write(BYTE data, const struct parsedname *pn) { BYTE data_masked = data | 0xFC ; BYTE cmd[] = { _1W_PIO_ACCESS_WRITE, data_masked, ~data_masked, }; BYTE chk[1]; BYTE confirm[] = { _1W_2413_CONFIRMATION, } ; struct transaction_log t[] = { TRXN_START, TRXN_WRITE3(cmd), TRXN_READ1(chk), TRXN_COMPARE(chk,confirm,1), TRXN_END, }; return BUS_transaction(t, pn) ; } // ledstate -> sense static UINT SENSED_state(UINT status_bits) { return ((status_bits & 0x01) >> 0) | ((status_bits & 0x04) >>1); } // ledstate -> latch static UINT LATCH_state(UINT status_bits) { return ((status_bits & 0x02) >> 1) | ((status_bits & 0x08) >>2); }
save and exit editor
goto topCreate file in editor:
node /usr/src/owfs_3.1p0/module/owlib/src/include/ow_arduino.h
insert the following into the file:
/* OWFS -- One-Wire filesystem OWHTTPD -- One-Wire Web Server Written 2003 Paul H Alfille email: paul.alfille@gmail.com Released under the GPL See the header file: ow.h for full attribution 1wire/iButton system from Dallas Semiconductor */ #ifndef OW_ARDU_H #define OW_ARDU_H #ifndef OWFS_CONFIG_H #error Please make sure owfs_config.h is included *before* this header file #endif #include "ow_standard.h" /* ------- Structures ----------- */ DeviceHeader(ARDUINO); #endif
save and exit editor
The following source files needs to be modified:
goto topModify file in editor:
node /usr/src/owfs_3.1p0/module/owlib/src/c/ow_tree.c
Modify the following into the file:
find in file:
Device2Tree( & d_Example_slave, ePN_real);
Insert after that line:
Device2Tree( & d_ARDUINO, ePN_real);
save and exit editor
goto topModify file in editor:
node /usr/src/owfs_3.1p0/module/owlib/src/include/ow_devices.h
Modify the following into the file:
find in file:
#include "ow_bae.h" // Pascal Baerten's PIC-based device
Insert after that line:
#include "ow_arduino.h" // My arduino-based slave
save and exit editor
goto topModify file in editor:
node /usr/src/owfs_3.1p0/module/owlib/src/include/Makefile
Modify the following into the file:
find line in file:
ow_arg.h \
Insert new line after that line:
ow_arduino.h \
save and exit editor
goto topModify file in editor:
node /usr/src/owfs_3.1p0/module/owlib/src/include/Makefile
Modify the following into the file:
find line in file:
ow_arg.h \
Insert line after that line:
ow_arduino.h \
save and exit editor
goto topModify file in editor:
node /usr/src/owfs_3.1p0/module/owlib/src/c/Makefile
Modify the following into the file:
find line in file:
ow_api.lo ow_avahi_announce.lo ow_avahi_browse.lo \
Change that line to:
ow_api.lo ow_arduino.lo ow_avahi_announce.lo ow_avahi_browse.lo \
find line in file:
ow_arg.c \
Insert new line after that line:
ow_arduino.c \
find line in file:
include ./$(DEPDIR)/ow_api.Plo
Insert new line after that line:
include ./$(DEPDIR)/ow_arduino.Plo
save and exit editor
goto topModify file in editor:
node /usr/src/owfs_3.1p0/module/owlib/src/c/Makefile.in
Modify the following into the file:
find line in file:
ow_api.lo ow_avahi_announce.lo ow_avahi_browse.lo \
Change that line to:
ow_api.lo ow_arduino.lo ow_avahi_announce.lo ow_avahi_browse.lo \
find line in file:
ow_api.c \
Insert new line after that line:
ow_arduino.c \
find line in file:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ow_api.Plo@am__quote@
Insert new line after that line:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ow_arduino.Plo@am__quote@
save and exit editor
goto topModify file in editor:
node /usr/src/owfs_3.1p0/module/owlib/src/c/Makefile.am
Modify the following into the file:
find line in file:
ow_api.c \
Insert line after that line:
ow_arduino.c \
save and exit editor
goto top./configure make -j 4 make install
Create a new Arduino project
Insert into the main file:
#include#include "Arduino.h" #include "LowLevel.h" #include "OneWireSlave.h" #include //------------------------------------------------------- // Definitions and constants //------------------------------------------------------- #define SRF_ADDRESS 0x70 // Address of the SRF02 #define CMD 0x00 // Command byte, values of 0 being sent with write have to be masked as a byte to stop them being misinterpreted as NULL this is a bug with arduino 1.0 #define RANGEBYTE 0x02 // Byte for start of ranging data #define CMD_LedRead 0xF5 #define CMD_LedWrite 0x5A #define CMD_ReadScratchpad 0xBE #define CMD_WriteScratchpad 0x4E #define CMD_CopyScratchpad 0x48 #define CMD_RecallMemory 0xB8 #define A 13 #define B 12 #define OW 2 Pin oneWireData(OW); // OneWire input Pin ledA(A); // LED A Pin ledB(B); // LED B const int i2c_interval = 1000; const byte owROM[7] = { 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }; //------------------------------------------------------- // Variables //------------------------------------------------------- class tankClass { public: float radius; float length; float height; int offset; int volume; int range; int level; } tank; tankClass tankDefault = { 32.5, 150.0, 137.0, 19, 0, 0, 0 }; // radius, length, height, offset, ....- byte LedStatus = 0; byte buffer[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; byte CurrCmd = 0x00; int pointer = 0; int loopcounter = 0; int newRange = 0; int lastRange = 0; String msg = "Starting"; byte memory[15]; //------------------------------------------------------- // Functions //------------------------------------------------------- byte * Int2Bytes(int val) { byte * buf = new byte[2]; buf[0] = (byte)(val & 255); buf[1] = (byte)(val >> 8); return buf; } void Int2Buf(byte * buf, int val) { buf[0] = (byte)(val & 255); buf[1] = (byte)(val >> 8); } int toInt(byte buf[]) { return (buf[0] + (buf[1] << 8)); } float toFloat(byte buf[]) { return (buf[0] + (buf[1] << 8)); } float area(float height, float radius) { float v = acos(1 - height / radius) * 2; // i radianer float retval = radius * radius / 2 * (v - sin(v)); return (retval); } void setLed(byte x) { byte a = x & 1; byte b = (x & 2) >> 1; if (a == 0) { a = 2; } if (b == 0) { b = 2; } byte c = (b << 2) + a; LedStatus = ((~c) << 4) + c; if (a & 1) { ledA.writeHigh(); } else { ledA.writeLow(); }; if (b & 1) { ledB.writeHigh(); } else { ledB.writeLow(); }; } void do_i2c() { lastRange = newRange; Wire.beginTransmission(SRF_ADDRESS); Wire.write(CMD); Wire.write(0x51); Wire.endTransmission(); delay(100); Wire.beginTransmission(SRF_ADDRESS); Wire.write(RANGEBYTE); Wire.endTransmission(); Wire.requestFrom(SRF_ADDRESS, 2); while (Wire.available() < 2); byte highByte = Wire.read(); byte lowByte = Wire.read(); newRange = ((highByte << 8) + lowByte); //newRange = getRange(); if ((newRange < lastRange * 1.2) && (newRange > lastRange * .8)) { tank.range = (newRange); } tank.level = tank.height + tank.offset - tank.range; float a = area(min(tank.level, tank.radius), tank.radius) + max(min(tank.level - tank.radius, tank.height - 2 * tank.radius), 0) * 2 * tank.radius + area(tank.radius, tank.radius) - area(min((tank.height - tank.level), tank.radius), tank.radius); tank.volume = round(a * tank.length / 1000); } //------------------------------------------------------- // Events and event Functions //------------------------------------------------------- void ow_reset() { // Reset receive state and buffer CurrCmd = 0x00; pointer = 0; } void owReceive(OneWireSlave::ReceiveEvent evt, byte data) { switch (evt) { case OneWireSlave::RE_Reset: ow_reset(); break; case OneWireSlave::RE_Error: ow_reset(); break; case OneWireSlave::RE_Byte: if (CurrCmd == 0x00) { switch (data) { case CMD_LedRead: OneWire.write(&LedStatus, 1, NULL); break; case CMD_LedWrite: case CMD_RecallMemory: case CMD_ReadScratchpad: case CMD_WriteScratchpad: case CMD_CopyScratchpad: CurrCmd = data; break; default: ow_reset(); break; } } else { buffer[pointer++] = data; switch (CurrCmd) { case CMD_LedWrite: if (pointer == 2) { setLed(buffer[1]); ow_reset(); }; break; case CMD_ReadScratchpad: // master will read if (pointer == 1) { Int2Buf(&memory[0], (int)(tank.offset * 10)); Int2Buf(&memory[2], (int)(tank.radius * 10)); Int2Buf(&memory[4], (int)(tank.height * 10)); Int2Buf(&memory[6], (int)(tank.length * 10)); Int2Buf(&memory[8], (int)(tank.volume * 10)); Int2Buf(&memory[10], (int)(tank.level * 10)); Int2Buf(&memory[12], (int)(tank.range * 10)); memory[14] = OneWire.crc8(&memory[0], 14); OneWire.write(&memory[0], 15, NULL); ow_reset(); }; break; case CMD_WriteScratchpad: // master will write if (pointer == 15) { tank.offset = (int) toInt(&buffer[1]) / 10; tank.radius = (float) toFloat(&buffer[3])/10; tank.height = (float) toFloat(&buffer[5])/10; tank.length = (float) toFloat(&buffer[7])/10; EEPROM.put(0, tank); ow_reset(); } break; case CMD_CopyScratchpad: if (pointer == 1) { ow_reset(); }; break; case CMD_RecallMemory: if (pointer == 1) { ow_reset(); }; break; default: ow_reset(); break; } } } } //------------------------------------------------------- // Setup //------------------------------------------------------- void setup() { ledA.outputMode(); // Init Led A ledB.outputMode(); // Init Led B Serial.begin(9600); LedStatus = (digitalRead(B)<<1) + digitalRead(A); setLed(LedStatus); // Set LED's to LedStatus //Serial.println(digitalRead(12)); //Serial.println(digitalRead(13)); EEPROM.get(0, tank); if ( isnan(tank.radius)) { tank = tankDefault; EEPROM.put(0, tank); } if ((tank.radius < 25.0) || (tank.length < 100.0) || (tank.height < 100.0)) { tank = tankDefault; EEPROM.put(0, tank); } Wire.begin(); // Init I2C OneWire.setReceiveCallback(&owReceive); // Set Onewire callback OneWire.begin(owROM, oneWireData.getPinNumber()); // Init Onewire listener delay(100); // Settle status //tank = { tankRadius, tankLength, tankHeight, Offset }; //newRange = getRange(); //tank.range = newRange; do_i2c(); } //------------------------------------------------------- // Main Loop //------------------------------------------------------- void loop() { //if (msg.length() > 0) { // clear(); // msg = ""; //} if (loopcounter == 0) { do_i2c(); /* Serial.write(0x0C); //Serial.write(01); delay(100); msg = "Volume: " + String(tank.volume, DEC); Serial.println( msg); msg = "Level: " + String (tank.level, DEC); Serial.println(msg); msg = "Range: " + String(tank.range, DEC); Serial.println(msg); msg = "Offset: " + String(tank.offset, DEC); Serial.println(msg); Serial.println("-----"); */ } if (loopcounter++ >= i2c_interval) loopcounter = 0; delay(1); }
exit, compile and upload
----------------------------------------------------------