• DHT22 temperature/humidity sensor

    andreas.betz12/04/2015 at 12:32 0 comments

    Since I'm still a bit stuck on the preamble part of the RF link (I need more precise timers to be able to sync to the incoming serial stream), I turned my attention to setting up a DHT22 sensor on my Photon instead.

    Communication with the DHT22 is via a one-wire protocol, though a special one: data bits are encoded in the length of HIGH state (~26us for 0, ~70us for 1) and are each preceded by a ~50us LOW state. To get the DHT22 to measure and send out its data, one needs to pull the data line low, then high. Adafruit have written a nice Arduino library for the DHT22, which I'm basing my code on.

    Essentially I'm reusing the SoftRx idea of capturing via the ICU and creating binary data from the time-list, plus some data line wiggling to initiate the sensor:

    ################################################################################
    # DHT22 on viperized Photon (V2)
    #
    # Created: 2015-12-04
    #
    # This software provides data readout from a DHT22 temperature+humidity sensor on any digital pin of an MCU running VIPER python.
    # This code was developed and tested on a viperized Photon board (Particle Photon).
    # It follows closely the Arduino code by Adafruit (https://github.com/adafruit/DHT-sensor-library).
    #
    # Copyright (c) 2015 A.C. Betz.  All right reserved. Developed using the VIPER IDE. 
    #
    # 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
    #
    #############################################################################################################################
    
    
    ############
    #########
    # DO THESE THINGS IN THE MAIN PROGRAM
    #########
    ############
    # import the streams module for USB serial port.
    import streams
    
    # open the default serial port
    streams.serial()
    
    #import ICU library
    import icu
    
    import timers
    
    ############
    #########
    # end of things to do in main program
    #########
    ############
    
    
    #########
    # functions
    #########
    
    def getDHT22data(_receivepin,_receivepinShort): #expects input like (D3.ICU,D3) [TODO: do some nifty string manipulation to get "D3" from D3.ICU]
        
        ### don't execute this more than once every 2s!
        
        timer1 = timers.timer()
        timer1.start()
        foo = 0
        
        DHT22_temp = 0
        DHT22_hum = 0
        BinListDHT22 = []
        timeListDHT22 = []
        
        #Go into high impedence state to let pull-up raise data line level andstart the reading process.
        pinMode(_receivepinShort,OUTPUT)
        digitalWrite(_receivepinShort, HIGH)
        timer1.reset()
        while timer1.get()<10:
            foo+=1
        #First set data line low for 10 milliseconds.
        digitalWrite(_receivepinShort, LOW)
        timer1.reset()
        while timer1.get()<10:
            foo+=1 # maybe change this while to one_shot?
        tmpICU = icu.capture(_receivepin,LOW,86,10000,time_unit=MICROS)#call to ICU seems to take some time, thus call *before* initiation is finished
        # End the start signal by setting data line high for [40 microseconds].
        digitalWrite(_receivepinShort, HIGH)
        pinMode(_receivepinShort,INPUT_PULLUP)
        
        # remove all even entries, they're just "start bits", discard 1st two entries
        for i in range(3,len(tmpICU),1):
            if i%2!=0: #these are the odd entries
                timeListDHT22.append(tmpICU[i])
        # convert to list of binaries
        for i in range(len(timeListDHT22)):
            if timeListDHT22[i] < 35:    # shouldn't be longer than 28us, but allow some wiggle room here
                BinListDHT22.append(0)
            else:
                BinListDHT22.append(1)    
        # extract hum, temp parts (16bits each)
        tmp_hum = BinListDHT22[0:16...
    Read more »

  • Manchester encoding via ICU

    andreas.betz11/25/2015 at 17:14 0 comments

    Receiving Manchester encoded data on the Photon's Input Capture Unit is now finally up and running. Well, the data bit at least (see below). I am still struggling to sync the receiver to the incoming serial clock, though. But that's a story for another day...

    Here's the code that let's me capture Manchester encoded data (sent from a Nano following closely the work done by mchr3k) on the Photon

    ################################################################################
    # ManchesterRx_v6
    #
    # Created: 2015-11-20
    #
    # This software creates a serial code receiver on any digital pin of an MCU running VIPER python.
    # Serial data needs to be Manchester encoded.
    # The Manchester en/decoding works along the lines of the Atmel Application Note "Manchester Coding Basics"
    # This code was developed and tested on a viperized Photon board (Particle Photon).
    #
    # Copyright (c) 2015 A.C. Betz.  All right reserved. Developed using the VIPER IDE. 
    #
    # 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
    #
    #############################################################################################################################
    
    
    ############
    #########
    # DO THESE THINGS IN THE MAIN PROGRAM
    #########
    ############
    # import the streams module for USB serial port.
    import streams
    
    # open the default serial port
    streams.serial()
    
    #import ICU library
    import icu
    
    ############
    #########
    # end of things to do in main program
    #########
    ############
    
    #############################################################################################################################
    
    ############
    #########
    # functions
    #########
    
    
    #########
    # convert RxBuffer to decimal
    #########
    def SoftRx_Bin2Dec(_BinList):
        global tmpDec
        tmpDec = 0
        for i in range(8):
            tmpDec += _BinList[i]*(2**i)
        return tmpDec
        _BinList[0:8] = []
    
    #########
    # round of values
    #########
    def round(_x):
        if _x - int(_x) >0.5: # int() always rounds towards zero & _x always >0
            return _x+1
        else:
            return _x
    
    #########
    # receive Manchester encoded data on the software serial port using InputCaptureUnit
    #########
    #
    # this code is waiting for 8 bits encoded in Manchester code flanked by a start and stop signal (each a [HIGH,LOW] tuple, i.e. a Manchester 0).
    # Manchester code represents logic 1/0 by rising/falling edge transition in the bit middle:
    # HIGH/1    __     LOW/0 __
    #        __|               |__
    #
    # 
    #########
    
    # receive an array of data bytes (without termination zeros in the stream!)
    
    def receiveManByteArray(_receivepin,_baudrate,_numberOfBytes): # expects inputs of the form (D1.ICU, 600, 10)
        duration1bitMICROS = int(round((1/_baudrate)*(10**6))) # 1s = 10^6 microseconds, rounded to next integer
        global RxBuffer
    
        RxBufferMAN = []
        BinListMAN = []
        
        tmpICU = icu.capture(_receivepin,LOW,16*_numberOfBytes,int(round((8*_numberOfBytes+0.75)*duration1bitMICROS)),time_unit=MICROS) # start capturing on falling edge, i.e. in the middle of the start "0"
        timeValues = [int(round(2*x/duration1bitMICROS)) for x in tmpICU]
        
        if len(tmpICU) > 0:  
    
            BinListMAN = [] # initiate list of binary values created from the microseconds list coming from the ICU, these are "half bits" though!
            for i in range(len(tmpICU)):
                if i%2==0:
                    for j in range(timeValues[i]):
                        BinListMAN.append(0)
                else:
                    for j in range(timeValues[i]):
                        BinListMAN.append(1)
     BinListMAN[0:1] = [] #remove first "bit"
    ...
    Read more »

  • Manchester Encoding

    andreas.betz11/10/2015 at 11:22 0 comments

    One of the goals of this project is to have sensor nodes transmitting data wireless to the Photon.

    Reading serial data from a digital pin is of course a good start for this, but it's not going to work very well with the cheap 433MHz modules that I have available at the moment: The 433MHz modules communicate in so called On-Off-Keying, i.e. 1 and 0 are represented by RF power on and off. In the absence of actual data transmission the receiver will then ramp up its RF gain till it sees a "signal" and we end up with noise "data". This might even happen for say a transmission of the form 0b10000000.

    A way to combat this is to use Manchester encoding: logic bits 0/1 are encoded as rising/ falling edges in the middle of the transmitted bit.

    There are good libraries for sending out Manchester encoded data on Arduino (or any other MCU IDE, I guess) and on the VIPER side of things we are also nearly there, as decoding Manchester data is all about the time between rising and falling edges.

    So the next step is to adapt the SoftRx_ICU routine for Manchester data and then get a first wireless transmission!

  • Software Serial Receive via the Input Capture Unit

    andreas.betz11/06/2015 at 16:58 0 comments

    Since the Photon is now running Python, which can do multi-threading, it's not necessarily the best idea to "interrupt & delay". That could grind everything to a halt. Not what we want...

    So instead I've been working on reading the incoming serial from the Photon's Input Capture Unit. Most of the Photon's digital pins are enabled for that and since it's a hardware (!) feature it means the rest of the code can continue to run (Provided the different parts are in different threads).

    So here's the code:

    ################################################################################
    # SoftRx_ICU
    #
    # Created: 2015-11-06 16:31:10
    #
    # This software creates a serial code receiver on any digital pin of an MCU running VIPER python.
    # It was developed and tested on a viperized Photon board (Particle Photon).
    #
    # Copyright (c) 2015 A.C. Betz.  All right reserved. Developed using the VIPER IDE. 
    #
    # 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
    #
    #############################################################################################################################
    
    
    ############
    #########
    # DO THESE THINGS IN THE MAIN PROGRAM
    #########
    ############
    # import the streams module for USB serial port.
    import streams
    
    # open the default serial port
    streams.serial()
    
    #import necessary libraries
    import timers
    import pwm #needed to enable hwtimers to run (?)
    import hwtimers
    import icu
    
    ############
    #########
    # end of things to do in main program
    #########
    ############
    
    #############################################################################################################################
    
    ############
    #########
    # functions
    #########
    
    
    
    #########
    # convert RxBuffer to decimal
    #########
    def SoftRx_Bin2Dec(_BinList):
        global tmpDec
        tmpDec = 0
        for i in range(8):
            tmpDec += _BinList[i]*(2**i)
        return tmpDec
        _BinList[0:8] = []
    
    #########
    # round off values
    #########
    def round(_x):
        if _x - int(_x) >0.5: # int() always rounds towards zero & _x always >0
            return _x+1
        else:
            return _x
    
        
    #########
    # receive data on the software serial port using InputCaptureUnit
    #########
    #
    # this code is waiting for an 8bit number flanked by a 2bit start and stop signal (each a [1,0] tuple)
    # so it's looking for this kind of signal:
    # __                           __
    #   |__--|--|--|--|--|--|--|--|  |__
    #
    # start| 8 bit binary         |stop
    #
    # as far as I'm aware the arduino SoftSerial library only sends a LOW start bit and a HIGH stop bit. 
    # The start bit would be OK for this code (it's looking for a falling edge to start capture), but it doesn't cope well with just a HIGH stop bit.
    #########
    
    def receive1byteICU(_receivepin,_baudrate): # _receivepin must be DigPin.ICU
    
        global RxBuffer
    
        duration1bitMICROS = int(round((1/_baudrate)*(10**6))) # 1s = 10^6 microseconds, rounded to next integer
    
        #wait for a falling edge to start capturing
        tmpICU = icu.capture(_receivepin,LOW,8,int(10*(round(1/_baudrate))*(10**6)),time_unit=MICROS)#returns a list of microseconds
        #create binary list from tmpICU
        timeListICU = [int(round(x/duration1bitMICROS)) for x in tmpICU] #list of times in units of bit duration
        BinListICU = [] # initiate list of binary values created from the microseconds list coming from the ICU
        for i in range(len(tmpICU)):
            if i%2==0:
                for j in range(timeListICU[i]):
                    BinListICU.append(0)
            else:
                for j in range(timeListICU[i]):
    ...
    Read more »

  • Arduino-like SoftwareSerial

    andreas.betz10/28/2015 at 17:02 0 comments

    In the Arduino IDE there's a very nice little library called NewSoftSerial, which allows to establish a serial link on any digital pin of your Arduino board. Very neat stuff indeed and often used for 433MHz RF links.

    Since this didn't exist on VIPER (yet), I've ported the "receive serial" part, using interrupt routines, to it (see below).

    Now, I'm sure that there's better ways of coding this, but for the moment I'm quite happy with it.

    ################################################################################
    # SoftRx
    #
    # Created: 2015-10-27 08:46:15.793526
    #
    ################################################################################
    
    #DO THESE THINGS IN THE MAIN PROGRAM
    
    # import the streams module for USB serial port.
    import streams
    
    # open the default serial port
    streams.serial()
    
    #import necessary libraries
    import timers
    import pwm #needed to enable hwtimers to run (?)
    import hwtimers
    
    global RxBuffer
    global RxByteDec
    
    ## end of things to do in main program
    
    #########
    #set up the SoftRx routine
    #########
    def SoftRx_setup(_rxpin, _baudrate, _timeUnit): # set _timeUnit=1 for microseconds, default is milliseconds
        global last_Rx_time
        global Rx_time
        global Rxpin
        global duration1bitMICROS
        global duration1bitMILLIS
        global RxBuffer
        global TimeUnit
        
        TimeUnit = _timeUnit
        Rxpin = _rxpin
        duration1bit = 1/_baudrate #duration of HIGH/LOW for 1 transmitted bit in seconds
        #
        duration1bitMILLIS = int(duration1bit*(10**3))
        duration1bitMICROS = int(duration1bit*(10**6)) # 1s = 10^6 microseconds, rounded to next integer
        RxBuffer = []
        last_Rx_time = 0 #used for interrupt settling
        Rx_time = timers.now() #used for interrupt settling, time in milliseconds
        
        ## debugging
    #    print("debugging")
    #    print(duration1bitMILLIS)
    #    print(3/2*duration1bitMILLIS)
    #    print(int(3/2*duration1bitMILLIS))
    
    #########
    # receive data on the software serial port
    #########
    def receive8bits():
        global last_Rx_time
        global Rx_time
        global Rxpin
        global duration1bitMICROS
        global duration1bitMILLIS
        global RxBuffer
    
        Rx_time = timers.now()
        time_test = Rx_time - last_Rx_time #time difference between last 2 interrupts, in milliseconds
        if time_test > int(10*duration1bitMILLIS): #interrupt fires at each HIGH->LOW, hence need to ignore any interrupt during reception of 1 byte = 1 start bit + 8 bits+1 stopbit
            if TimeUnit==1:
                hwtimers.sleep_micros(int(duration1bitMICROS/2)) # read at bit centres
            else:
                sleep(int(duration1bitMILLIS/2)) #only works for baudrates <1000
                #sleep(1)
            for i in range(9): #now read 9 bits (start+8) at their respective centres
                tmp = digitalRead(Rxpin)
                if len(RxBuffer)>255: #don't store more than 32 bytes (=256 bits) in buffer
                    RxBuffer[0] = [] #remove oldest bit
                RxBuffer.append(tmp)
                #print(tmp) #use for debugging
                if TimeUnit==1:
                    hwtimers.sleep_micros(duration1bitMICROS) # start bit: LOW for duration=1/baudrate, then read at bit centres
                else:
                    sleep(duration1bitMILLIS) #only works for baudrates <1000
                    #sleep(2)
            last_Rx_time = Rx_time
            RxBuffer[0:1] = [] #remove start bit
            return RxBuffer
        else:
            return
    
    #########
    # start up the serial port
    #########
    def SoftRx_start():
        onPinFall(Rxpin,receive8bits) #start listening on the SoftSerial port
    
    #########
    # convert RxBuffer to decimal
    #########
    def SoftRx_Bin2Dec(_BinList):
        tmpDec = 0
        for i in range(8):
            tmpDec += _BinList[i]*(2**i)
        return tmpDec
    
    
    ##the following is just to test the above...
    SoftRx_setup(D0,500,1) # set up all the necessary bits
    SoftRx_start() #start listening on the SoftSerial port
    
    
    while True:
        sleep(1000)
        if len(RxBuffer)>0:
            RxData = RxBuffer[0:8]
            RxBuffer[0:8] = []
            RxByteDec = SoftRx_Bin2Dec(RxData)
            print(RxData)
            print(RxByteDec)
            print("---")

  • Viperizing the Photon

    andreas.betz10/28/2015 at 16:40 0 comments

    OK, what the hell is he talking about, I hear you say.

    A few months ago I bought a new wifi enabled MCU (then) called Spark Photon (https://docs.particle.io/datasheets/photon-datasheet/). It comes with its own Arduino inspired IDE and some cloud API. Which it seemed to me everything runs through, all my code, sensor readings, the lot. I'm not a fan of putting stuff on the cloud and only flashing over-the-air, so I looked for alternatives in terms of firmware. Enter VIPER (www.viperize.it), a Python based firmware for MCUs.

    After connecting the Photon to the Particle cloud once (and with great difficulty, I must say), it was time to follow the VIPER guys; instructions and "viperize" the Photon. With the right DFU drivers and VIPER installed it went ahead just fine. Even the wifi connection was established near out-of-the box :)