From 6ee448e33e243711375c65e85d5383b3d68ca732 Mon Sep 17 00:00:00 2001 From: Thomas Schmid Date: Tue, 17 Jul 2007 18:45:08 +0200 Subject: [PATCH] Added SHT11 driver for TMote. I adapted the driver for the SHT15 from the MDA300 sensor board for the TMote. --- modules/sensordrivers/sht11/Makefile | 7 + modules/sensordrivers/sht11/sht11.c | 589 ++++++++++++++++++++ modules/sensordrivers/sht11/sht11.h | 72 +++ .../unit_test/modules/sensordrivers/sht11/Makefile | 9 + .../unit_test/modules/sensordrivers/sht11/README | 13 + .../unit_test/modules/sensordrivers/sht11/pysos.py | 605 +++++++++++++++++++++ .../modules/sensordrivers/sht11/sht11_test.c | 190 +++++++ .../modules/sensordrivers/sht11/sht11_test.py | 37 ++ 8 files changed, 1522 insertions(+) create mode 100644 modules/sensordrivers/sht11/Makefile create mode 100644 modules/sensordrivers/sht11/sht11.c create mode 100644 modules/sensordrivers/sht11/sht11.h create mode 100644 modules/unit_test/modules/sensordrivers/sht11/Makefile create mode 100644 modules/unit_test/modules/sensordrivers/sht11/README create mode 100644 modules/unit_test/modules/sensordrivers/sht11/pysos.py create mode 100644 modules/unit_test/modules/sensordrivers/sht11/sht11_test.c create mode 100644 modules/unit_test/modules/sensordrivers/sht11/sht11_test.py diff --git a/modules/sensordrivers/sht11/Makefile b/modules/sensordrivers/sht11/Makefile new file mode 100644 index 0000000..2469e26 --- /dev/null +++ b/modules/sensordrivers/sht11/Makefile @@ -0,0 +1,7 @@ +# PROJ is the file name of your module. +PROJ = sht11 +SB = sht11 +ROOTDIR = ../../.. + +include $(ROOTDIR)/modules/Makerules + diff --git a/modules/sensordrivers/sht11/sht11.c b/modules/sensordrivers/sht11/sht11.c new file mode 100644 index 0000000..841aac4 --- /dev/null +++ b/modules/sensordrivers/sht11/sht11.c @@ -0,0 +1,589 @@ +/* -*- Mode: C; tab-width:4 -*- */ +/* ex: set ts=4 shiftwidth=4 softtabstop=4 smartindent: */ + +/** + * Driver Support for the SHT11 on TMote + * + * This module currently provides support for measuring + * temperature and humidity from the SHT11 chip. + * + * \author Kapy Kangombe, John Hicks, James Segedy {jsegedy@gmail.com} + * \date 07-2005 + * Ported driver from TinyOS to SOS + * + * \author Roy Shea (roy@cs.ucla.edu) + * \date 06-2006 + * \date 05-2007 + * Ported driver to current version of SOS + * + * \author Thomas Schmid (thomas.schmid@ucla.edu) + * \date 07-2007 + * Ported driver from SHT11 to SHT11 + */ + +#include +#include "sht11.h" +#define LED_DEBUG +#include +#include + +//// +// Hardware specific defines +// +// These may be moved to a driver file later on in the future. +//// +// adr command r/w +#define STATUS_REG_WRITE 0x06 // 000 0011 0 +#define STATUS_REG_READ 0x07 // 000 0011 1 +#define MEASURE_TEMPERATURE 0x03 // 000 0001 1 +#define MEASURE_HUMIDITY 0x05 // 000 0010 1 +#define RESET 0x1e // 000 1111 0 + + +/** + * SHT11 data and clock line operations + * + * \note The SHT11 uses a modified version of the I2C interface. + * + * \todo Move some platform specific pin definitions into an external platform + * include file. Continue by udating this driver to use the SHIT15_* pin and + * port defines rather than direct pin and port defines. + */ + +#define SHT11_PORT P1 +#define SHT11_DIRECTION P1DIR +#define SHT11_READ_PIN P1IN +#define SHT11_PIN_NUMBER 5 +#define SHT11_PIN 0x10 + +#define set_data() SETBITHIGH(P1OUT, 5) +#define clear_data() SETBITLOW(P1OUT, 5) +#define set_clock() SETBITHIGH(P1OUT, 6) +#define clear_clock() SETBITLOW(P1OUT, 6) +#define enable_sht11() SETBITHIGH(P1OUT, 7) +#define disable_sht11() SETBITLOW(P1OUT, 7) +#define make_data_output() SETBITHIGH(P1DIR, 5) +#define make_data_input() SETBITLOW(P1DIR, 5) +#define make_clock_output() SETBITHIGH(P1DIR, 6) +#define make_enable_output() SETBITHIGH(P1DIR, 7) + + + +//// +// Local enums +//// + +/** + * Type of reading that is requisted from the SHT11 + */ +enum sht11_command +{ + TEMPERATURE, + HUMIDITY, +}; + + +/** + * Ack flag + */ +enum sht11_ack +{ + ACK, + NOACK, +}; + + +/** + * Local timer + */ +enum +{ + SHT11_TEMPERATURE_TIMER, + SHT11_HUMIDITY_TIMER, +}; + +#define SHT11_TEMPERATURE_TIME 240L +#define SHT11_HUMIDITY_TIME 70L + +// SOS module specifc funictions and state + +static int8_t sht11_msg_handler(void *start, Message *e); + +typedef struct +{ + uint8_t calling_module; +} sht11_state_t; + +static const mod_header_t mod_header SOS_MODULE_HEADER = { + .mod_id = SHT11_ID, + .state_size = sizeof(sht11_state_t), + .num_sub_func = 0, + .num_prov_func = 0, + .platform_type = HW_TYPE /* or PLATFORM_ANY */, + .processor_type = MCU_TYPE, + .code_id = ehtons(SHT11_ID), + .module_handler = sht11_msg_handler, +}; + + +//// +// Local function prototypes +//// + +static void delay(); + +static bool write_byte(uint8_t value); + +static uint8_t read_byte(enum sht11_ack ack); + +static void start_transmission(); + +static void connection_reset(); + +static bool measure(enum sht11_command command); + +static bool get_data(uint16_t *data, uint8_t *checksum, enum sht11_command command); + + +//// +// Code, glorious code! +//// + +/** Main message handler for SHT11 + * + * \param state ID of the last calling module + * + * \param msg Incoming message + * + * This kernel module handles the following mellage types from other + * applications: + * + * \li SHT11_GET_TEMPERATURE Generates a SHT11_TEMPERATURE message that is sent + * to the calling applications and contains the temperature on the sht11 chip. + * + * \li SHT11_GET_HUMIDITY Generates a SHT11_HUMIDITY message that is sent to + * the calling application and contains the humidity on the sht11 chip. + * + * All of these messages return SOS_OK to the kernel if all goes well. An + * error code is returned if something goes wrong. Perhaps you could have + * guessed that. + * + */ +static int8_t sht11_msg_handler(void *state, Message *msg) { + + sht11_state_t *s = (sht11_state_t *)state; + + /**

Types of messages handled by the SHT11 module

+ */ + switch(msg->type) + { + /** + * \par MSG_INIT + * Start up the SHT11. This is easy, since the SHT11 started when the + * mote was powerd on. + */ + + case MSG_INIT: + { + s->calling_module = 0; + + LED_DBG(LED_RED_ON); + LED_DBG(LED_GREEN_ON); + LED_DBG(LED_YELLOW_ON); + make_enable_output(); + enable_sht11(); + break; + } + + + /** + * \par MSG_FINAL + * Stick a fork in it, cause this module is done. + */ + case MSG_FINAL: + { + LED_DBG(LED_RED_OFF); + LED_DBG(LED_GREEN_OFF); + LED_DBG(LED_YELLOW_OFF); + disable_sht11(); + break; + } + + + /** + * Grab data from the SHT11 + */ + case MSG_TIMER_TIMEOUT: + { + uint8_t checksum; + uint16_t *data; + bool no_error; + + data = (uint16_t *)sys_malloc(sizeof(uint16_t)); + + MsgParam *param = (MsgParam*) (msg->data); + switch (param->byte) { + + case SHT11_TEMPERATURE_TIMER: + no_error = get_data(data, &checksum, TEMPERATURE); + if (no_error == false) + { + *data = 0xFFFF; + connection_reset(); + } + LED_DBG(LED_GREEN_TOGGLE); + sys_post(s->calling_module, + SHT11_TEMPERATURE, + sizeof(uint16_t), + data, + SOS_MSG_RELEASE); + break; + + case SHT11_HUMIDITY_TIMER: + no_error = get_data(data, &checksum, HUMIDITY); + if (no_error == false) + { + *data = 0xFFFF; + connection_reset(); + } + LED_DBG(LED_GREEN_TOGGLE); + sys_post(s->calling_module, + SHT11_HUMIDITY, + sizeof(uint16_t), + data, + SOS_MSG_RELEASE); + break; + + default: + sys_free(data); + break; + + } + break; + } + + + /** + * \par SHT11_GET_TEMPERATURE or SHT11_GET_HUMIDITY + * Request a temperature or humidity reading from the SHT11. + * + * \par + * \todo Add in check of checksum? + */ + case SHT11_GET_TEMPERATURE: + case SHT11_GET_HUMIDITY: + { + bool no_error; + + s->calling_module = msg->sid; + + // Connect to the SHT11 + /** + * \todo Is a rest neeeded before talking to the sht11? Once + * the code is up and running, look into removing it. + */ + make_clock_output(); + connection_reset(); + + // Take a measruement. If an error occures reset the SHT11 + // and send 0xFFFF as the "data" to the calling module. + if(msg->type == SHT11_GET_TEMPERATURE) { + no_error = measure(TEMPERATURE); + } else if (msg->type == SHT11_GET_HUMIDITY) { + no_error = measure(HUMIDITY); + } else { + LED_DBG(LED_RED_TOGGLE); + LED_DBG(LED_GREEN_TOGGLE); + LED_DBG(LED_YELLOW_TOGGLE); + return SOS_OK; + //return -EINVAL; + } + + if (no_error == false) + { + connection_reset(); + return -EINVAL; + } + break; + } + + + /** + * \par default + * Unknowen message type. Bummer. + */ + default: + { + return -EINVAL; + } + + } + + return SOS_OK; + +} + + +//// +// Implementation of local functions +//// + + +/** + * Hardware specific delay. This is target towards the mica2 and micaZ motes. + * + * These will probably be moved into a driver file at some point, since this + * is hardware specific. + */ +static void delay() { + asm volatile ("nop" ::); + asm volatile ("nop" ::); + asm volatile ("nop" ::); +} + + +/** + * Write a byte to the SHT11 + * + * \param byte Byte to write to the SHT11 + * + * \return True if there were no errors + * + */ +static bool write_byte(uint8_t byte) +{ + HAS_CRITICAL_SECTION; + + uint8_t i; + + ENTER_CRITICAL_SECTION(); + for (i=0x80; i>0; i = i>>1) + { + if (i & byte) { + set_data(); + } else { + clear_data(); + } + + set_clock(); + delay(); + delay(); + clear_clock(); + } + + set_data(); + make_data_input(); + delay(); + set_clock(); + + // SHT11 pulls line low to signal ACK so 0 is good + if (GETBIT(SHT11_READ_PIN, SHT11_PIN_NUMBER) != 0) { + return false; + } + + delay(); + clear_clock(); + make_data_input(); + + LEAVE_CRITICAL_SECTION(); + return true; +} + + +/** + * Reads a byte from the SHT11 + * + * \param ack Enable or disable ack for the byte + * + * \return Byte read from the SHT11 + * + */ +static uint8_t read_byte(enum sht11_ack ack) +{ + HAS_CRITICAL_SECTION; + + uint8_t i; + uint8_t val; + + val = 0; + + ENTER_CRITICAL_SECTION(); + + set_data(); + make_data_input(); + delay(); + + for (i=0x80; i>0; i = i>>1) + { + set_clock(); + delay(); + + // SHT11 pulls line low to signal 0, otherwise, we need to put on one + // into the correct bit of val + if (GETBIT(SHT11_READ_PIN, SHT11_PIN_NUMBER) != 0) { + val = (val | i); + } + clear_clock(); + } + + make_data_output(); + + if (ack == ACK) { + clear_data(); + } + + delay(); + set_clock(); + delay(); + clear_clock(); + LEAVE_CRITICAL_SECTION(); + + return val; +} + +/** + * Generates a transmission start + * + * The lines should look like: + * ____ _____ + * DATA: |____| + * __ __ + * SCK : __| |__| |____ + */ +static void start_transmission() +{ + HAS_CRITICAL_SECTION; + ENTER_CRITICAL_SECTION(); + make_data_output(); + set_data(); + clear_clock(); + delay(); + set_clock(); + delay(); + clear_data(); + delay(); + clear_clock(); + delay(); + set_clock(); + delay(); + set_data(); + delay(); + clear_clock(); + LEAVE_CRITICAL_SECTION(); +} + + +/** + * Reset a connection if anything should go wrong + */ +static void connection_reset() +{ + HAS_CRITICAL_SECTION; + + uint8_t i; + + ENTER_CRITICAL_SECTION(); + make_data_output(); + set_data(); + clear_clock(); + for (i=0; i<9; i++) + { + set_clock(); + delay(); + clear_clock(); + } + LEAVE_CRITICAL_SECTION(); +} + + +/** + * Begin a measurement of either temperature or humidity from the SHT11 + * + * \param command Either TEMPERATURE to request a temperature measurement or + * HUMIDITY to measure humidity + * + * \return True if there were no errors + * + */ +static bool measure(enum sht11_command command) +{ + bool no_error; + + start_transmission(); + switch (command) + { + case TEMPERATURE: + no_error = write_byte(MEASURE_TEMPERATURE); + if(no_error == false) { + LED_DBG(LED_RED_TOGGLE); + return false; + } + sys_timer_start(SHT11_TEMPERATURE_TIMER, SHT11_TEMPERATURE_TIME, TIMER_ONE_SHOT); + break; + + case HUMIDITY: + no_error = write_byte(MEASURE_HUMIDITY); + if(no_error == false) { + LED_DBG(LED_RED_TOGGLE); + return false; + } + sys_timer_start(SHT11_HUMIDITY_TIMER, SHT11_HUMIDITY_TIME, TIMER_ONE_SHOT); + break; + + default: + return false; + } + + return true; +} + + +/** + * Get the measurement from the SHT11 + * + * \param data Pointer to a 2-byte data buffer + * + * \param checksum Pointer to a 1-byte checksum + * + * \param command Either TEMPERATURE to request a temperature measurement or + * HUMIDITY to measure humidity + * + * \return True if there were no errors + * + * \note The timing used to delay calls to this function are dependent on the + * measurement size being requested from the SHT11. This code assumes the + * defaults of 12-bit temperature readings and 14-bit humidity readings. + * + */ +static bool get_data(uint16_t *data, uint8_t *checksum, enum sht11_command command) +{ + + HAS_CRITICAL_SECTION; + + make_data_input(); + delay(); + + // SHT11 pulls line low to signal that the data is ready + if (GETBIT(SHT11_READ_PIN, SHT11_PIN_NUMBER) != 0) { + return false; + } + + *data = (read_byte(ACK)<<8); + *data += read_byte(ACK); + *checksum = read_byte(NOACK); + + ENTER_CRITICAL_SECTION(); + LEAVE_CRITICAL_SECTION(); + + return true; +} + +#ifndef _MODULE_ +mod_header_ptr sht11_get_header() +{ + return sos_get_header_address(mod_header); +} +#endif + + + diff --git a/modules/sensordrivers/sht11/sht11.h b/modules/sensordrivers/sht11/sht11.h new file mode 100644 index 0000000..1bc187f --- /dev/null +++ b/modules/sensordrivers/sht11/sht11.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width:4 -*- */ +/* ex: set ts=4 shiftwidth=4 softtabstop=4 smartindent: */ + +/** + * Driver Support for the SHT11 + * + * This module currently provides support for measuring + * temperature and humidity from the SHT11 chip. + * + * \author Kapy Kangombe, John Hicks, James Segedy {jsegedy@gmail.com} + * \date 07-2005 + * Ported driver from TinyOS to SOS + * + * \author Roy Shea (roy@cs.ucla.edu) + * \date 06-2006 + * Ported driver to current version of SOS + * + * \author Thomas Schmid (thomas.schmid@ucla.edu) + * \date 07-2007 + * Ported driver from SHT11 to SHT11 + */ + +#ifndef _SHT11_H_ +#define _SHT11_H_ + + +/** + * ID of the SHT11 module + */ +#define SHT11_ID (DFLT_APP_ID1) + + +/** + * Constant used to mark the start point of message types HANDLED by the + * SHT11 + */ +#define SHT11_MSG_IN (MOD_MSG_START + 0) + +/** + * Constant used to mark the start point of message types PROVIDED by the + * SHT11 + */ +#define SHT11_MSG_OUT (SHT11_MSG_IN + 2) + +/** + * Constant used to mark the end of the messages associated with the SHT11 + */ +#define SHT11_MSG_END (SHT11_MSG_OUT + 2) + + +/** + * SHT11 specific mesage types HANDLED by the SHT11 from other modules + */ +enum +{ + SHT11_GET_TEMPERATURE = (SHT11_MSG_IN + 0), + SHT11_GET_HUMIDITY = (SHT11_MSG_IN + 1), +}; + + +/** + * DS2438 specific mesage types SENT by the DS2438 to other modules + */ +enum +{ + SHT11_TEMPERATURE = (SHT11_MSG_OUT + 0), + SHT11_HUMIDITY = (SHT11_MSG_OUT + 1), +}; + + +#endif // _SHT11_H_ + diff --git a/modules/unit_test/modules/sensordrivers/sht11/Makefile b/modules/unit_test/modules/sensordrivers/sht11/Makefile new file mode 100644 index 0000000..00116ae --- /dev/null +++ b/modules/unit_test/modules/sensordrivers/sht11/Makefile @@ -0,0 +1,9 @@ + +# PROJ is the file name of your module. +PROJ = sht11_test +ROOTDIR = ../../../../.. + + +include $(SOSROOT)/modules/Makerules + + diff --git a/modules/unit_test/modules/sensordrivers/sht11/README b/modules/unit_test/modules/sensordrivers/sht11/README new file mode 100644 index 0000000..062d5ad --- /dev/null +++ b/modules/unit_test/modules/sensordrivers/sht11/README @@ -0,0 +1,13 @@ +This test uses a mote connected a UART (probably via a programming board) +and a MDA300 or MDA320 sensor board. To run the test: + +- Install the blank configuration onto the mote + +- Use sos_tool to install ../../modules/sensordrivers/sht15/sht15.mlf + +- Use sos_tool to install modules/unit_test/modules/sensordrivers/sht15/sht15_test.mlf + +- Run modules/unit_test/modules/sensordrivers/sht15/sht15_test.py to observe + output + + diff --git a/modules/unit_test/modules/sensordrivers/sht11/pysos.py b/modules/unit_test/modules/sensordrivers/sht11/pysos.py new file mode 100644 index 0000000..0db5ea2 --- /dev/null +++ b/modules/unit_test/modules/sensordrivers/sht11/pysos.py @@ -0,0 +1,605 @@ +""" +PySOS version 1.51 - March 22 2007 +Authors: Thiago Teixeira and Neeraj Singh. + +This module allows you to connect to a SOS server on any host +and port, as well as send and receive SOS messages. + + +INITIALIZATION: +=============== + +Make sure you are running the SOS server, then create a new instance: + +>>> import pysos +>>> srv = pysos.sossrv() # you may enter host=... and port=... here +>>> # localhost and 7915 are default + + +SENDING MESSAGES: +================= + +There are 3 different ways to send messages. They aren't too +different, and it's up to your personal preference which one to use: + + +1ST METHOD: +----------- + +>>> data = pysos.pack('>> +>>> srv.post(daddr = 5, saddr = 3, did = 128, +... sid = 128, type = 32, data = data) + +Any of these can be omitted, in which case the defaults specified +with set_message_defaults() are utilized. + + +2ND METHOD: +----------- + +This method is largely the same as the previous, but it separates +the message creation from the act of sending it: + +>>> m = pysos.msg(daddr = 5, saddr = 3, did = 128, +... sid = 128, type = 32, data = data) +>>> +>>> srv.post_msg(m) + + +3RD METHOD: +----------- + +If you prefer to use SOS's post_net syntax, you may do so like this: + +>>> srv.post_net(128, 128, 32, 4, data, 0, 5) + +In this case, saddr is the one specified with set_message_defaults(), +or 0xFFFE by default. This is because post_net does not let you specify +your saddr in SOS. + +Also note that the "length" and "flags" parameters are ignored. + + +RECEIVING MESSAGES: +=================== + +There are 2 different methods you can use. The first one is +synchronous (blocking) and the 2nd asynchronous -- it allows you to +register listeners and then run a non-blocking method to start listening +for messages. You can use both of these methods with the same sossrv. + + +1ST METHOD (synchronous): +------------------------- + +>>> msg = srv.listen(did = 128, sid = 128, +... daddr = 0x14, saddr = 0x16, +... type = 32, nreplies = 5, timeout = 3.0) + +This method returns the first matching messages. It returns the message as +a dictionary with keys 'did', 'sid', 'daddr', 'saddr', 'type, 'length', +and 'data'. To cast msg['data'] into a tuple, you may use the unpack() +method, as such: + +>>> data = pysos.unpack('>> srv.register_trigger(func, did = 128, +... sid = 128, daddr = 0x14, +... saddr = 0x16, type = 32) + +Where you may omit any parameter (except func) to match all messages, +irrespective of that parameter's value. That is, None is a wildcard. + +At any point, you may use the deregister_trigger() method to remove +triggers from the pysos instance. When deregistering a trigger, None +is once again used as wildcard. + + +RPC-STYLE COMMUNICATIONS: +========================= + +You can also do an RPC-style call, which posts a message to the network +and returns the response message(s): + +>>> replylist = srv.post_rpc_msg(m, rtype=36, nreplies=10, timeout=5.0) + +The above command creates a message dictionary (through sossrv.msg_dict) +which is sent to all the nodes. We collect up to 10 replies with message +type 36 in the variable msglist. The replies are in a list of message +dicts. If 5 seconds elapse, we just return the messages obtained thus +far. + +For those who do not wish to first create a message dict (the variable +called 'm' in the example above), there is the post_rpc() method: + +>>> post_rpc(did = 0x97, daddr = 13, type = 32, +... rsid = 0x97, rsaddr = 13, rtype = 40, +... timeout = 3, nreplies = 5) + + +MORE INFORMATION: +================= + +Use each method's help function for more details. +""" + +from threading import Thread, Lock, Condition +from struct import pack, unpack +import socket +from time import strftime + +BCAST_ADDRESS = 0xFFFF #move this to a higher scope, since it's useful + +class sossrv: + + SOS_HEADER_SIZE = 8 + BCAST_ADDRESS = 0xFFFF + MOD_MSG_START = 32 + + def __init__(self, host=None, port=None, nid=0xFFFE, pid=128, verbose=False): + + if host: self.host = host + else: self.host = 'localhost' + + if port: self.port = port + else: self.port = 7915 + + self.daddr = self.BCAST_ADDRESS + self.saddr = nid + self.did = pid + self.sid = pid + self.type = self.MOD_MSG_START + self.verbose = verbose + + self._retries = 0 + + self._listening = False + self._listenthread = None + self._leftoverbytes = '' + + self._syncPkt = [] # variable for passing packets to a sync listener + self._syncTrig = None + self._syncCV = Condition() # condition variable for sync receive + + self._triggers = [] + self._trgLock = Lock() # synchronizes reg/dereg against matching + + self._sock = None + + if self.verbose: print('Connecting to sossrv at %s:%d' % (self.host, self.port)) + self._connect() + + # necessary when running in fast systems to ensure that + # pysos is connected to sossrv when the constructor returns + while True: + if self._listening: break + + + def __del__(self): + + self.disconnect() + + + def disconnect(self): + + if self.verbose: print('Disconnecting from sossrv...') + + if self._listening: + self._sock.shutdown(socket.SHUT_RDWR) + self._listening = False + self._sock.close() + + if self._listenthread: self._listenthread.join() + + + def reconnect(self, host=None, port=None): + """ + Run this if you restart sossrv and need to reconnect. + """ + + if host == None : host = self.host + if port == None : port = self.port + + self._connect(host=host, port=port, retries=self.retries) + + + def set_msg_defaults(self, did=None, sid=None, daddr=None, saddr=None, type=None): + """ + Use this method to specify the default parameters for sending + messages. This allows you to type less arguments when calling + methods such as post() or msg(). + """ + + if did == None : did = self.did + if sid == None : sid = self.sid + if daddr == None : daddr = self.daddr + if saddr == None : saddr = self.saddr + if type == None : type = self.type + + + def post(self, daddr=None, saddr=None, did=None, sid=None, type=None, data=None): + """ + Sends a message. Unspecified fields will be sent using the + defaults set when instantiating sossrv or by use of + set_message_defaults() method. + + The data argument must be a str (use the pack() method to + build strings from tuples). + """ + + if did == None : did = self.did + if sid == None : sid = self.sid + if daddr == None : daddr = self.daddr + if saddr == None : saddr = self.saddr + if type == None : type = self.type + if data == None : data = '' + + self._sock.send(pack(' 1 and (not timeout): + raise Exception, 'You should provide a timeout if you want multiple replies.' + + # Only one thread can be synchronously listening at a time + assert self._syncTrig == None and self._syncPkt == [] + + ret = [] + + self._syncCV.acquire() + self._syncTrig = (rdid, rsid, rdaddr, rsaddr, rtype, None) # make a trigger packet + + self.post(daddr=daddr, saddr=saddr, did=did, sid=sid, type=type, data=data) + + while nreplies > 0: + self._syncCV.wait(timeout) + + if len(self._syncPkt): + # this is None on timeout or error. (hdr, data) otherwise + nreplies -= len(self._syncPkt) + msgdict = [_make_msg_dict(*pkt) for pkt in self._syncPkt] + ret.extend(msgdict) + self._syncPkt = [] + else: break + + self._syncTrig = None + self._syncCV.release() + + return ret + + + def post_rpc_msg(self, msgdict, + rdid = None, rsid = None, rdaddr = None, + rsaddr = None, rtype = None, timeout = None, + nreplies = 1): + """ + post_rpc_msg(msgdict, + rdid = None, rsid = None, rdaddr = None, + rsaddr = None, rtype = None, timeout = None, + nreplies = 1) + + returns [msg0, msg1, ...] + + This method allows you to send a message and wait for the + replies. The parameters starting with the letter 'r' describe + what the expected reply looks like. The timeout parameter is + required when nreplies != 1. + + msgdict is a dictionary rescribing the message and can be + created with the msg() method. + """ + + return self.post_rpc( + did = msgdict['did'], + sid = msgdict['sid'], + daddr = msgdict['daddr'], + saddr = msgdict['saddr'], + type = msgdict['type'], + data = msgdict['data'], + rdid = rdid, rsid = rsid, rdaddr = rdaddr, + rsaddr = rsaddr, rtype = rtype, timeout = timeout, + nreplies = nreplies) + + + def listen(self, did=None, sid=None, daddr=None, saddr=None, type=None, timeout=None, nreplies=1): + """ + listen(did=None, sid=None, daddr=None, saddr=None, + type=None, timeout=None, nreplies=1) + + returns [msg0, msg1, ...] + + This is a blocking method that returns the first matching message. + Timeout is specified in floating point seconds. + """ + + t = (did, sid, daddr, saddr, type) + + if not self._listening: + raise Exception, 'The sossrv instance is disconnected.?' + + if nreplies > 1 and (not timeout): + raise Exception, 'You should provide a timeout if you want multiple replies.' + + # Only one thread can be synchronously listening at a time + assert self._syncTrig == None and self._syncPkt == [] + + ret = [] + + self._syncCV.acquire() + self._syncTrig = t + + while nreplies > 0: + self._syncCV.wait(timeout) + nreplies -= len(self._syncPkt) + if len(self._syncPkt): + # this is None on timeout or error. (hdr, data) otherwise + msgdict = [_make_msg_dict(*pkt) for pkt in self._syncPkt] + ret.extend(msgdict) + self._syncPkt = [] + + else: break + + self._syncTrig = None + self._syncCV.release() + + return ret + + + def register_trigger(self, func, did=None, sid=None, daddr=None, saddr=None, type=None): + """ + Register a trigger (i.e. listener) to execute 'func(hdr, data)' upon + receiving a matching message.'. + + NOTE: Because of the way Python works, if you change a function *after* registering + it, you must deregister then re-register it! + """ + + self._trgLock.acquire() + self._triggers.append((did, sid, daddr, saddr, type, func)) + self._trgLock.release() + + + def deregister_trigger(self, func=None, did=None, sid=None, daddr=None, saddr=None, type=None): + """ + Deregisters ALL matching triggers, so if no arguments are provided + all triggers are deregistered. + """ + + pattern = (did, sid, daddr, saddr, type, func) + newtriggers = [] + + self._trgLock.acquire() + + for t in self._triggers: + if not _match_trigger(t, pattern): newtriggers.append(t) + + self._triggers = newtriggers + self._trgLock.release() + + + def msg(self, daddr=None, saddr=None, did=None, sid=None, type=None, data=''): + """ + Returns a properly-formatted message dictionary with default values + for any parameters not provided. Defaults can be set with set_defaults(). + """ + + if did == None : did = self.did + if sid == None : sid = self.sid + if daddr == None : daddr = self.daddr + if saddr == None : saddr = self.saddr + if type == None : type = self.type + + return {'did' : did, 'sid' : sid, 'daddr' : daddr, + 'saddr' : saddr, 'type' : type, 'length' : len(data), + 'data' : data, 'flags' : 0} + + + def _connect(self, host=None, port=None, retries=0): + """ + Internal: connects to the sossrv + """ + + if host == None: host = self.host + else: self.host = host + + if port == None: port = self.port + else: self.port = port + + self.retries = retries + + self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + while retries >= 0: + try: + self._sock.connect((host, port)) + retries = -1 + except Exception, e: + if retries <= 0: raise IOError, "Can't connect to SOS server: " + str(e) + retries -= 1 + continue + + self._start_listening() + + + + def _start_listening(self): + """ + Internal: Starts a thread to listen to all incomming messages. This must + be called in order to receive any data. + """ + if (self._listenthread == None) or (not self._listenthread.isAlive()): + self._listenthread = Thread(name='SOSSRVpktrecv', target=self._listen_for_triggers) + self._listenthread.setDaemon(True) + self._listenthread.start() + + + def _receive_message_tuple(self): + """ + Internal: Returns the next packet received by the SOS server as a header + tuple and data str. Use the unpack() method to cast the data + into the required format. + """ + + hdrstr = self._leftoverbytes + tmpbuf = '' + + while len(hdrstr) < self.SOS_HEADER_SIZE : + tmpbuf = self._sock.recv(self.SOS_HEADER_SIZE - len(tmpbuf)) + if tmpbuf != '' : hdrstr += tmpbuf + else : raise socket.error, 'Socket reset by peer.' + + # we do this in case we get a bit of the data string along with hdrstr + hdrstr = hdrstr[:self.SOS_HEADER_SIZE] + data = hdrstr[self.SOS_HEADER_SIZE:] + + hdr = unpack(' +#include +#define LED_DEBUG +#include + +//// +// Local enumerations +//// + +/** + * Current functionality of the SHT11 kernel module that is being tested + */ +enum test_state { + UNIT_SHT11_GET_TEMPERATURE = 0, + UNIT_SHT11_GET_HUMIDITY = 1, +}; + + +/** + * Single timer used to iterativly trigger testing of functionality of the + * SHT11 + */ +enum { + UNIT_SHT11_TIMER, +}; + + + +//// +// Module specific state and headers +//// + +#define UNIT_SHT11_ID (SHT11_ID + 1) +#define UNIT_SHT11_TIMER_PERIOD 3000 + + +/** + * Module specific state used to track the current functionality of the SHT11 + * driver being testied + */ +typedef struct { + enum test_state state; +} app_state_t; + + +static int8_t sht11_test_msg_handler(void *start, Message *e); + +static const mod_header_t mod_header SOS_MODULE_HEADER = { + .mod_id = UNIT_SHT11_ID, + .state_size = sizeof(app_state_t), + .num_sub_func = 0, + .num_prov_func = 0, + .platform_type = HW_TYPE /* or PLATFORM_ANY */, + .processor_type = MCU_TYPE, + .code_id = ehtons(UNIT_SHT11_ID), + .module_handler = sht11_test_msg_handler, +}; + + + +//// +// Code! We love code! +//// + + +/** Base message handler + * + * This module uses the driver to pull each of these forms of data from the + * SHT11. A new form of data is collected about UNIT_SHT11_TIMER_PERIOD ms. + */ +static int8_t sht11_test_msg_handler(void *state, Message *msg) { + + void *data; + uint8_t length; + app_state_t *s = (app_state_t *)state; + + switch (msg->type){ + + case MSG_INIT: + { + + sys_timer_start(UNIT_SHT11_TIMER, UNIT_SHT11_TIMER_PERIOD, TIMER_REPEAT); + s->state = UNIT_SHT11_GET_TEMPERATURE; + + break; + } + + + case MSG_FINAL: + { + sys_timer_stop(UNIT_SHT11_TIMER); + break; + } + + + case MSG_TIMER_TIMEOUT: + { + sys_led(LED_YELLOW_TOGGLE); + switch (s->state) { + + case UNIT_SHT11_GET_TEMPERATURE: + sys_post_value(SHT11_ID, SHT11_GET_TEMPERATURE, 0, 0); + s->state = UNIT_SHT11_GET_HUMIDITY; + break; + + case UNIT_SHT11_GET_HUMIDITY: + sys_post_value(SHT11_ID, SHT11_GET_HUMIDITY, 0, 0); + s->state = UNIT_SHT11_GET_TEMPERATURE; + break; + + default: + s->state = UNIT_SHT11_GET_TEMPERATURE; + break; + + } + break; + } + + case SHT11_TEMPERATURE: + + sys_led(LED_YELLOW_TOGGLE); + length = msg->len; + data = sys_msg_take_data(msg); + sys_post_uart(UNIT_SHT11_ID, SHT11_TEMPERATURE, + length, data, SOS_MSG_RELEASE, BCAST_ADDRESS); + break; + + case SHT11_HUMIDITY: + + sys_led(LED_YELLOW_TOGGLE); + length = msg->len; + data = sys_msg_take_data(msg); + sys_post_uart(UNIT_SHT11_ID, SHT11_HUMIDITY, + length, data, SOS_MSG_RELEASE, BCAST_ADDRESS); + break; + + default: + return -EINVAL; + } + + + return SOS_OK; +} + +#ifndef _MODULE_ +mod_header_ptr sht11_test_get_header() +{ + return sos_get_header_address(mod_header); +} +#endif + + + diff --git a/modules/unit_test/modules/sensordrivers/sht11/sht11_test.py b/modules/unit_test/modules/sensordrivers/sht11/sht11_test.py new file mode 100644 index 0000000..a41b593 --- /dev/null +++ b/modules/unit_test/modules/sensordrivers/sht11/sht11_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +import pysos + +SHT11_TEMPERATURE = 34 +SHT11_HUMIDITY = 35 +SHT11_MOD = 130 + +def sht11_temperature(msg): + # Note that pysos.unpack returns a tuple. The comma after the lval + # notes that we want to unpack the singleton tuple. + temperature, = pysos.unpack('