Commit d41368b8 authored by Julien Leduc's avatar Julien Leduc

Initial GPLv3 code base for the DCES project

parent 0017a3d0
--------------------
dces-dtrhf-ser1ch-v1
--------------------
Here is what the name stands for:
Data Center Environmental Sensor sensing Dust, Temperature, Relative Humidity and Fan speed sending results on SERial 1 CHannel - version 1.
This is a long name, but it precisely defines what is implemented in the project.
Compiling and flashing the firmware
-----------------------------------
Some prerequites, to compile this firmware, you need to install the ``arduino-core`` package, the full IDE is not needed, because it is simple an faster to build from the commandline:
::
$ yum instal arduino-core
Before going any further you need to adapt the configuration file to your setup: edit ``dces-dtrhf-ser1ch-v1.conf`` in ``dces-dtrhf-ser1ch-v1/arduino/``.
Then to build and flash the arduino board, simply run ``build.sh`` from command line:
::
$ cd dces-dtrhf-ser1ch-v1/arduino
$ ./build.sh
It is only going to build ``dces-dtrhf-ser1ch-v1/arduino/final/final.ino`` using ``dces-dtrhf-ser1ch-v1/arduino/final/Makefile``.
To clean the project, just ``make clean`` from ``dces-dtrhf-ser1ch-v1/arduino/final`` directory.
If ``UPLOAD=true`` in ``dces-dtrhf-ser1ch-v1.conf``, the firmware is going to be flashed on the arduino board.
Configuring the firmware
------------------------
Firmware configuration is done from the arduino serial port using ``SerialCommand`` library.
When the firmware starts it displays measured data by default:
::
$ picocom /dev/ttyACM0
port is : /dev/ttyACM0
flowcontrol : none
baudrate is : 9600
parity is : none
databits are : 8
escape is : C-a
local echo is : no
noinit is : no
noreset is : no
nolock is : no
send_cmd is : sz -vv
receive_cmd is : rz -vv
imap is :
omap is :
emap is : crcrlf,delbs,
Terminal ready
8471, 8384, 446, 26.69, 31.37, 196, 0, 960
8419, 8448, 163, 26.69, 31.44, 196, 0, 960
...
#!/bin/bash
##############################################################################
#
# This file is part of the DCES project.
# See http://www.ohwr.org/projects/dces-dtrhf-ser1ch-v1
#
# Copyright (C) 2016 CERN
# All rights not expressly granted are reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# @author DCES Dev team, dces-dev@cern.ch
#############################################################################
. ./dces-dtrhf-ser1ch-v1.conf 2>/dev/null
cd final
if [[ $UPLOAD = "true" ]]
then
echo "Compiling and upgrading arduino firmware"
make BOARD=$BOARD SERIALDEV=$DEVICE upload
else
echo "Compiling only"
make BOARD=$BOARD SERIALDEV=$DEVICE
fi
###
# Example configuration file for dust sensor firmware update
###
###
# Configuration section for firmware compilation/upgrade
###
[MAKE_SECTION]
# UPLOAD="true" automatically build and upload firmware upon rpm installation
UPLOAD="false"
# Arduino board type for dust sensor as specified in /usr/share/arduino/hardware/arduino/boards.txt
BOARD="mega2560"
# TTY device for serial communications
DEVICE="/dev/ttyACM0"
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
I2C.h - I2C library
Copyright (c) 2011-2012 Wayne Truchsess. All right reserved.
Rev 5.0 - January 24th, 2012
- Removed the use of interrupts completely from the library
so TWI state changes are now polled.
- Added calls to lockup() function in most functions
to combat arbitration problems
- Fixed scan() procedure which left timeouts enabled
and set to 80msec after exiting procedure
- Changed scan() address range back to 0 - 0x7F
- Removed all Wire legacy functions from library
- A big thanks to Richard Baldwin for all the testing
and feedback with debugging bus lockups!
Rev 4.0 - January 14th, 2012
- Updated to make compatible with 8MHz clock frequency
Rev 3.0 - January 9th, 2012
- Modified library to be compatible with Arduino 1.0
- Changed argument type from boolean to uint8_t in pullUp(),
setSpeed() and receiveByte() functions for 1.0 compatability
- Modified return values for timeout feature to report
back where in the transmission the timeout occured.
- added function scan() to perform a bus scan to find devices
attached to the I2C bus. Similar to work done by Todbot
and Nick Gammon
Rev 2.0 - September 19th, 2011
- Added support for timeout function to prevent
and recover from bus lockup (thanks to PaulS
and CrossRoads on the Arduino forum)
- Changed return type for stop() from void to
uint8_t to handle timeOut function
Rev 1.0 - August 8th, 2011
This is a modified version of the Arduino Wire/TWI
library. Functions were rewritten to provide more functionality
and also the use of Repeated Start. Some I2C devices will not
function correctly without the use of a Repeated Start. The
initial version of this library only supports the Master.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if(ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#endif
#include <inttypes.h>
#ifndef I2C_h
#define I2C_h
#define START 0x08
#define REPEATED_START 0x10
#define MT_SLA_ACK 0x18
#define MT_SLA_NACK 0x20
#define MT_DATA_ACK 0x28
#define MT_DATA_NACK 0x30
#define MR_SLA_ACK 0x40
#define MR_SLA_NACK 0x48
#define MR_DATA_ACK 0x50
#define MR_DATA_NACK 0x58
#define LOST_ARBTRTN 0x38
#define TWI_STATUS (TWSR & 0xF8)
#define SLA_W(address) (address << 1)
#define SLA_R(address) ((address << 1) + 0x01)
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define MAX_BUFFER_SIZE 32
class I2C
{
public:
I2C();
void begin();
void end();
void timeOut(uint16_t);
void setSpeed(uint8_t);
void pullup(uint8_t);
void scan();
uint8_t available();
uint8_t receive();
uint8_t write(uint8_t, uint8_t);
uint8_t write(int, int);
uint8_t write(uint8_t, uint8_t, uint8_t);
uint8_t write(int, int, int);
uint8_t write(uint8_t, uint8_t, char*);
uint8_t write(uint8_t, uint8_t, uint8_t*, uint8_t);
uint8_t read(uint8_t, uint8_t);
uint8_t read(int, int);
uint8_t read(uint8_t, uint8_t, uint8_t);
uint8_t read(int, int, int);
uint8_t read(uint8_t, uint8_t, uint8_t*);
uint8_t read(uint8_t, uint8_t, uint8_t, uint8_t*);
private:
uint8_t start();
uint8_t sendAddress(uint8_t);
uint8_t sendByte(uint8_t);
uint8_t receiveByte(uint8_t);
uint8_t stop();
void lockUp();
uint8_t returnStatus;
uint8_t nack;
uint8_t data[MAX_BUFFER_SIZE];
static uint8_t bytesAvailable;
static uint8_t bufferIndex;
static uint8_t totalBytes;
static uint16_t timeOutDelay;
};
extern I2C I2c;
#endif
#######################################
# Syntax Coloring Map For I2C
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
I2C KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
end KEYWORD2
timeOut KEYWORD2
setSpeed KEYWORD2
pullup KEYWORD2
scan KEYWORD2
write KEYWORD2
read KEYWORD2
available KEYWORD2
receive KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
I2c KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
\ No newline at end of file
/*******************************************************************************
SerialCommand - An Arduino library to tokenize and parse commands received over
a serial port.
Copyright (C) 2011-2013 Steven Cogswell <steven.cogswell@gmail.com>
http://awtfy.com
See SerialCommand.h for version history.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
***********************************************************************************/
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "SerialCommand.h"
#include <string.h>
#ifndef SERIALCOMMAND_HARDWAREONLY
#include <SoftwareSerial.h>
#endif
// Constructor makes sure some things are set.
SerialCommand::SerialCommand()
{
usingSoftwareSerial=0;
strncpy(delim," ",MAXDELIMETER); // strtok_r needs a null-terminated string
term='\r'; // return character, default terminator for commands
numCommand=0; // Number of callback handlers installed
clearBuffer();
}
#ifndef SERIALCOMMAND_HARDWAREONLY
// Constructor to use a SoftwareSerial object
SerialCommand::SerialCommand(SoftwareSerial &_SoftSer)
{
usingSoftwareSerial=1;
SoftSerial = &_SoftSer;
strncpy(delim," ",MAXDELIMETER); // strtok_r needs a null-terminated string
term='\r'; // return character, default terminator for commands
numCommand=0; // Number of callback handlers installed
clearBuffer();
}
#endif
//
// Initialize the command buffer being processed to all null characters
//
void SerialCommand::clearBuffer()
{
for (int i=0; i<SERIALCOMMANDBUFFER; i++)
{
buffer[i]='\0';
}
bufPos=0;
}
// Retrieve the next token ("word" or "argument") from the Command buffer.
// returns a NULL if no more tokens exist.
char *SerialCommand::next()
{
char *nextToken;
nextToken = strtok_r(NULL, delim, &last);
return nextToken;
}
// This checks the Serial stream for characters, and assembles them into a buffer.
// When the terminator character (default '\r') is seen, it starts parsing the
// buffer for a prefix command, and calls handlers setup by addCommand() member
void SerialCommand::readSerial()
{
// If we're using the Hardware port, check it. Otherwise check the user-created SoftwareSerial Port
#ifdef SERIALCOMMAND_HARDWAREONLY
while (Serial.available() > 0)
#else
while ((usingSoftwareSerial==0 && Serial.available() > 0) || (usingSoftwareSerial==1 && SoftSerial->available() > 0) )
#endif
{
int i;
boolean matched;
if (usingSoftwareSerial==0) {
// Hardware serial port
inChar=Serial.read(); // Read single available character, there may be more waiting
} else {
#ifndef SERIALCOMMAND_HARDWAREONLY
// SoftwareSerial port
inChar = SoftSerial->read(); // Read single available character, there may be more waiting
#endif
}
#ifdef SERIALCOMMANDDEBUG
Serial.print(inChar); // Echo back to serial stream
#endif
if (inChar==term) { // Check for the terminator (default '\r') meaning end of command
#ifdef SERIALCOMMANDDEBUG
Serial.print("Received: ");
Serial.println(buffer);
#endif
bufPos=0; // Reset to start of buffer
token = strtok_r(buffer,delim,&last); // Search for command at start of buffer
if (token == NULL) return;
matched=false;
for (i=0; i<numCommand; i++) {
#ifdef SERIALCOMMANDDEBUG
Serial.print("Comparing [");
Serial.print(token);
Serial.print("] to [");
Serial.print(CommandList[i].command);
Serial.println("]");
#endif
// Compare the found command against the list of known commands for a match
if (strncmp(token,CommandList[i].command,SERIALCOMMANDBUFFER) == 0)
{
#ifdef SERIALCOMMANDDEBUG
Serial.print("Matched Command: ");
Serial.println(token);
#endif
// Execute the stored handler function for the command
(*CommandList[i].function)();
clearBuffer();
matched=true;
break;
}
}
if (matched==false) {
(*defaultHandler)();
clearBuffer();
}
}
if (isprint(inChar)) // Only printable characters into the buffer
{
buffer[bufPos++]=inChar; // Put character into buffer
buffer[bufPos]='\0'; // Null terminate
if (bufPos > SERIALCOMMANDBUFFER-1) bufPos=0; // wrap buffer around if full
}
}
}
// Adds a "command" and a handler function to the list of available commands.
// This is used for matching a found token in the buffer, and gives the pointer
// to the handler function to deal with it.
void SerialCommand::addCommand(const char *command, void (*function)())
{
if (numCommand < 30) {
#ifdef SERIALCOMMANDDEBUG
Serial.print(numCommand);
Serial.print("-");
Serial.print("Adding command for ");
Serial.println(command);
#endif
strncpy(CommandList[numCommand].command,command,SERIALCOMMANDBUFFER);
CommandList[numCommand].function = function;
numCommand++;
} else {
// In this case, you tried to push more commands into the buffer than it is compiled to hold.
// Not much we can do since there is no real visible error assertion, we just ignore adding
// the command
#ifdef SERIALCOMMANDDEBUG
Serial.println("Too many handlers - recompile changing MAXSERIALCOMMANDS");
#endif
}
}
// This sets up a handler to be called in the event that the receveived command string
// isn't in the list of things with handlers.
void SerialCommand::addDefaultHandler(void (*function)())
{
defaultHandler = function;
}
/*******************************************************************************
SerialCommand - An Arduino library to tokenize and parse commands received over
a serial port.
Copyright (C) 2011-2013 Steven Cogswell <steven.cogswell@gmail.com>
http://awtfy.com
Version 20131021A.
Version History:
May 11 2011 - Initial version
May 13 2011 - Prevent overwriting bounds of SerialCommandCallback[] array in addCommand()
defaultHandler() for non-matching commands
Mar 2012 - Some const char * changes to make compiler happier about deprecated warnings.
Arduino 1.0 compatibility (Arduino.h header)
Oct 2013 - SerialCommand object can be created using a SoftwareSerial object, for SoftwareSerial
support. Requires #include <SoftwareSerial.h> in your sketch even if you don't use
a SoftwareSerial port in the project. sigh. See Example Sketch for usage.
Oct 2013 - Conditional compilation for the SoftwareSerial support, in case you really, really
hate it and want it removed.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
***********************************************************************************/
#ifndef SerialCommand_h
#define SerialCommand_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
// If you want to use SerialCommand with the hardware serial port only, and want to disable
// SoftwareSerial support, and thus don't have to use "#include <SoftwareSerial.h>" in your
// sketches, then uncomment this define for SERIALCOMMAND_HARDWAREONLY, and comment out the
// corresponding #undef line.
//
// You don't have to use SoftwareSerial features if this is not defined, you can still only use
// the Hardware serial port, just that this way lets you get out of having to include
// the SoftwareSerial.h header.
//#define SERIALCOMMAND_HARDWAREONLY 1
#undef SERIALCOMMAND_HARDWAREONLY
#ifdef SERIALCOMMAND_HARDWAREONLY
#warning "Warning: Building SerialCommand without SoftwareSerial Support"
#endif
#ifndef SERIALCOMMAND_HARDWAREONLY
#include <SoftwareSerial.h>
#endif
#include <string.h>
#define SERIALCOMMANDBUFFER 16
#define MAXSERIALCOMMANDS 30
#define MAXDELIMETER 2
#define SERIALCOMMANDDEBUG 1
#undef SERIALCOMMANDDEBUG // Comment this out to run the library in debug mode (verbose messages)
class SerialCommand
{
public:
SerialCommand(); // Constructor
#ifndef SERIALCOMMAND_HARDWAREONLY
SerialCommand(SoftwareSerial &SoftSer); // Constructor for using SoftwareSerial objects
#endif
void clearBuffer(); // Sets the command buffer to all '\0' (nulls)
char *next(); // returns pointer to next token found in command buffer (for getting arguments to commands)
void readSerial(); // Main entry point.
void addCommand(const char *, void(*)()); // Add commands to processing dictionary
void addDefaultHandler(void (*function)()); // A handler to call when no valid command received.
private:
char inChar; // A character read from the serial stream
char buffer[SERIALCOMMANDBUFFER]; // Buffer of stored characters while waiting for terminator character
int bufPos; // Current position in the buffer
char delim[MAXDELIMETER]; // null-terminated list of character to be used as delimeters for tokenizing (default " ")
char term; // Character that signals end of command (default '\r')
char *token; // Returned token from the command buffer as returned by strtok_r
char *last; // State variable used by strtok_r during processing
typedef struct _callback {
char command[SERIALCOMMANDBUFFER];
void (*function)();
} SerialCommandCallback; // Data structure to hold Command/Handler function key-value pairs
int numCommand;
SerialCommandCallback CommandList[MAXSERIALCOMMANDS]; // Actual definition for command/handler array
void (*defaultHandler)(); // Pointer to the default handler function
int usingSoftwareSerial; // Used as boolean to see if we're using SoftwareSerial object or not
#ifndef SERIALCOMMAND_HARDWAREONLY
SoftwareSerial *SoftSerial; // Pointer to a user-created SoftwareSerial object
#endif
};
#endif //SerialCommand_h
/******************************************************************************
*
* This file is part of the DCES project.
* See http://www.ohwr.org/projects/dces-dtrhf-ser1ch-v1
*
* Copyright (C) 2016 CERN
* All rights not expressly granted are reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* @author DCES Dev team, dces-dev@cern.ch
*****************************************************************************/
// Dust measurement example:
// You need to include lib_dust.h
#include <lib_dust.h>
float dust=0;
float avgDust, medDust, stdevDust;
void setup(){
Serial.begin(9600);
initDust(); // Don't forget to call initDust() to set A0 pin as input pin
}
void loop(){
float myAvgDust, myMedDust, myStdevDust; // my own calirated values
dust = measureDust(avgDust, medDust, stdevDust); // Then, you just have to call measureDust() whenever you want, and get the dust level
Serial.print("Dust value averaged by the dust library: ");
Serial.println(dust);
// finer custom made calibration for the sensor
myAvgDust = ((avgDust*0.0049)-0.0256)*120000-38000;
myMedDust = ((medDust*0.0049)-0.0256)*120000-38000;
myStdevDust = stdevDust*588;
Serial.println("My own calibrated values: ");
Serial.println(myAvgDust);
Serial.println(myMedDust);
Serial.println(myStdevDust);
delay(2000);
}
/******************************************************************************
*
* This file is part of the DCES project.
* See http://www.ohwr.org/projects/dces-dtrhf-ser1ch-v1
*
* Copyright (C) 2016 CERN
* All rights not expressly granted are reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* @author DCES Dev team, dces-dev@cern.ch
*****************************************************************************/
#include <lib_dust.h>
// GLOBALS
unsigned int dustPin=0;
unsigned int ledPower=2;
unsigned int delayTime=280;
unsigned int delayTime2=40;
float offTime=9680; // so that delayTime + delayTime2 + offTime = 10ms
unsigned int skip_measurements=50; // skip the first 50 mesurements of a series
unsigned int dustVal=0;
float voltage = 0;
float dustdensity = 0;
float ppmpercf = 0;
unsigned int dustValues[NB_MEAS]; // array storing all the measured values (10 bit)
// since we'll be doing an average over several values, we can increase the precision of each measurement to more bits
// with a simple bit shift without fearing an overflow
unsigned long sum; // to store the sum of measured dust values (4 bytes): NB_MEAS < 4194303 to avoid overflow => OK
// INITIALIZE DUST PIN
void initDust(void){
pinMode(ledPower,OUTPUT);
}
// MEASURE DUST
float measureDust(float &avgRetDust, float &medRetDust, float &stdevRetDust){
unsigned int i,j; // loop indexes
unsigned int temp; // temporary dust value
float medianDust, varDust, stdevDust, avgDust; // some statistical values
// skip initial measurements see dust_calib
for (i=0; i<skip_measurements; i++) {
digitalWrite(ledPower,LOW); // power on the LED
delayMicroseconds(delayTime);
delayMicroseconds(delayTime2);
digitalWrite(ledPower,HIGH); // turn the LED off
delayMicroseconds(offTime);
}
// Let s do the real measurements
for (i=0; i<NB_MEAS; i++) {
digitalWrite(ledPower,LOW); // power on the LED
delayMicroseconds(delayTime);
dustValues[i]=analogRead(dustPin) << 6; // read the dust value and shift the 10bit ADC value by 6 bits
delayMicroseconds(delayTime2);
digitalWrite(ledPower,HIGH); // turn the LED off
delayMicroseconds(offTime);
}
#ifdef DEBUG
// display values in measurement order first
// because later the array is going to be sorted to find the median
Serial.println("all values:");
for (i=0; i<NB_MEAS; i++) {
Serial.print(i);
Serial.print(" ");