Welcome to the Parallax Discussion Forums, sign-up to participate.
rem test_temp2.bas
'
'
dim indata%
waitcnt(getcnt() + 10_000_000) 'Wait for terminal screen
pausems 2000 'Wait for sensor to stabilize
direction(2) = output
output(2) = 1 'Pull high
pausems 1000
output(2) = 0 'Pull low
pausems 0000_0500 'uS
output(2) = 1 'Pull high
direction(2) = input 'Chnge to input
pausems 1000
output(2) = indata% 'Capture incoming
print "Exp Data! "; indata% 'Print out the data captured
''******************************************
''* Title: DHTnn_Object *
''* Modified from DHT21_Object - *
''* Walter T. Mosscrop 2016 *
''* Original Author: Gregg Erickson 2012 *
''* See MIT License for Related Copyright *
''* See end of file and objects for . *
''* related copyrights and terms of use *
''* This object draws upon code from other*
''* OBEX objects such as servoinput.spin *
''* and DHT C++ from Adafruit Industries *
''* *
''* This object reads the temperature and *
''* humidity from an DHT11 or DHT22 *
''* using a unique 1-wire serial protocol *
''* with 5 byte packets where 0s are 26uS *
''* long and 1s are 70uS. *
''* *
''* The object automatically returns the *
''* temperature and humidiy to variables *
''* memory every few seconds as Deg C and *
''* relative percent respectively. It also*
''* return an error byte where true means *
''* the data received had correct parity *
''* *
''* The read loop has been reworked to *
''* stabilize the timing and reduce the *
''* number of invalid reads due to timing *
''* issues *
''******************************************
{
Vcc──────────────Red (Power)
│
10K (Pull-Up Resistor)
│
Prop Pin ────────Yellow (Data)
VSS──────────────Black (Ground)
}
CON
MAX_SAMPLES = 40 ' 40 one-bit samples
VAR
long APin,Device
long MeasurementStack[50]
long clkpermicro, clkminpulse
long measurementCog, measuring
long MeasurementValues[MAX_SAMPLES]
long temperaturePtr, humidityPtr, statusPtr
OBJ
f : "FloatMath"
PUB StartDHTnn(devicePin,DeviceModel,TempPtr,HumPtr,StatPtr) : result
APin := devicePin
Device := DeviceModel
measurementCog := -1
{{
This method lauches the Measuring code in a new cog
to read the DHTnn autonomously with variables updated
every few seconds
}}
MeasurementInit(APin, @measuring, @MeasurementValues)
temperaturePtr := TempPtr
humidityPtr := HumPtr
statusPtr := StatPtr
PUB Stop
{{
stop cog if in use
}}
if measurementCog => 0
cogstop(measurementCog)
Pub DHTnn_Read | Data[5], Temp, Humid, ByteCount,BitCount,Pulsewidth,Parity,ptr,tr,hr
{{
This method reads the DHTnn device autonomously with variables located at
the pointers updated every few seconds
}}
'Apin is data line in from DHTnn
'Data[5] bytes received in order from DHTnn
'DATA = upper 8 bits of RH data
' + lower 8 bits of RH data
' + upper 8 bits of temperature data
' + lower 8 bits of temperature data
' + 8 bits check-sum (equals other 4 bytes added)
'ByteCount - Index counter for data Bytes
'BitCount - Index counter for bits within data bytes
'Pulsewidth - Width of bits received from DHTnn, 26uS = 0, 70S=1, in clock ticks
'parity - Boolean - parity for data from DHTnn
'clkpermicro - number of clock ticks per microsecond
'clkminpulse - the number of clock ticks for a pulsewidth value to be considered a 1
clkpermicro := clkfreq / 1_000_000
clkminpulse := clkpermicro * 48 ' Minimum 48 us 'high' pulse
waitcnt(clkfreq*2+cnt) ' Pause to allow DHTnn to stabilize
DIRA[Apin]~~ ' Set selected Pin to output
OUTA[Apin]~~ ' Pull Up selected Pin to start sequence
Repeat ByteCount from 0 to 4
Data[ByteCount]:=0 ' Clear Data of Each Byte Before Input
waitcnt(clkfreq/4+cnt) ' Pause to allow DHTnn to stabilize
' Send a low to the DHTnn to request data
DIRA[Apin]~~ ' Set selected Pin to output
OUTA[Apin]~ ' Pull Down selected Pin for 500 uS to Request Data
if device == 11
waitcnt((clkpermicro * 20_000) + cnt) ' hold 20ms
else
waitcnt(clkfreq / 2000 + cnt) ' Pause for 500uS
OUTA[Apin]~~ ' Return Pin to High
DIRA[Apin]~ ' Set Pin to Input and Release Control to DHTnn
' DHTnn reponds with a ready signal (80uS low, 80uS high, 40 uS low, before data)
waitpne(|<Apin,|<Apin,0) ' Wait for low, high, low sequence
waitpeq(|<Apin,|<Apin,0)
waitpne(|<Apin,|<Apin,0)
MeasurementStart
' DHTnn will send 40 high bits where 0 is 26uS and 1 is 70uS
repeat until measuring == 0
ptr := 0
Repeat ByteCount from 0 to 4 ' Store Data in 5 Bytes
Repeat BitCount from 7 to 0 ' Receive Data by Bit, MSB to LSB
pulsewidth := LONG[@MeasurementValues][ptr++]
if pulsewidth > clkminpulse ' If Pulse > min pulse then bit is 1 else 0
Data[ByteCount]|=|<BitCount ' Store the bit in byte
' Check Parity
parity := ((data[0]+data[1]+data[2]+data[3])& $ff)==(data[4]) 'Last byte equals sum of first four bytes
' Calculate Temperature
if parity
if (Device == 11)
tr := data[2] & $7f ' Pull from data2, mask MSB Bit
else
tr := data[2] ' Pull from data2
tr <<= 8 ' Put into upper byte
tr += data[3] ' Add lower byte
Temp := f.FFloat(tr)
if (Device == 11)
Temp := f.FDiv(Temp, 256.0) ' Convert to DHT11 value
else
Temp := f.FMul(Temp, 0.1) ' Convert to DHT22 value
' Calculate Humidity
if parity
Humid:=data[0] ' Pull from data0
Humid<<=8 ' Put into upper byte
Humid+=data[1] ' Add lower byte
Humid := f.FFloat(Humid)
if (Device == 11)
Humid := f.FDiv(Humid, 256.0) ' Convert to DHT11 value
else
Humid := f.FMul(Humid, 0.1) ' Convert to DHT22 value
' Return values to addresses provided in pointers
if parity
Long[temperaturePtr] := temp ' Temperature
Long[humidityPtr] := humid ' Humidity
Long[statusPtr] := 1 ' Parity ok
else
Long[statusPtr]:= 0 ' Error
' Cog relies on these arguments, do not remove
pub MeasurementInit(pAPin, pMeasuringStatus, pMeasurementValues)
measurementCog := cognew(@MeasureCog, @pAPin)
pub MeasurementStart
Long[statusPtr] := -1 ' We're updating values
LONGFILL(@MeasurementValues, MAX_SAMPLES, 0)
measuring := 1
DAT
ORG 0
MeasureCog
mov p1, par ' Get data pointer
rdlong APinNum, p1 ' Get APin number
add p1, #4 ' Get next pointer
rdlong measuringStatusPtr, p1 ' Get pointer to measuring value
add p1, #4 ' Get next pointer
rdlong arrayPtrSave, p1 ' Get monitor array pointer
mov APinMask, #1 ' Get pin mask
shl APinMask, APinNum
' Used for timing pulses
movi ctra, #%11111 ' Setup counter (always)
mov frqa, #1 ' One count per tick
' Used for timeout in case of device failure
movi ctrb, #%11111 ' Setup counter (always)
mov frqb, #1 ' One count per tick
wait
rdlong t1, measuringStatusPtr wz ' Check "measuring" value
if_z jmp #wait ' Wait until "measuring" value nonzero
' Prepare for measurement
mov arrayPtr, arrayPtrSave ' Set up array pointer
mov t2, #0 ' Reset counter
mov phsb, #0 ' Reset timeout
measure
' Handle low-to-high (pulse gap)
mov t1, #0 wc ' Reset carry for waitpeq
waitpeq APinMask, APinMask ' Wait for high state
mov t1, phsb ' Have we timed out?
cmp t1, timeoutTicks wz, wc
if_a jmp #done ' Yes, set done & start over
mov phsa, #0 ' Reset counter
' Time high-to-low (pulse)
waitpne APinMask, APinMask ' Wait for low state
mov t1, phsa ' Save counter value in t1, can't write directly to hub
wrlong t1, arrayPtr ' Save duration value
add arrayPtr, #4 ' Set up for next array value
mov t1, phsb ' Have we timed out?
cmp t1, timeoutTicks wz, wc
if_a jmp #done ' Yes, set done & start over
add t2, #1 ' Count values
cmp maxSample, t2 wz ' Have we reached the limit?
if_ne jmp #measure ' No, back for more
done mov t1, #0 ' Yes, set status value to zero
wrlong t1, measuringStatusPtr ' Update status value
jmp #wait ' We're done monitoring
APinNum long 0
maxSample long MAX_SAMPLES
measuringStatusPtr long 0
arrayPtr long 0
arrayPtrSave long 0
timeoutTicks long 5 * 80_000_000 ' Assumes 80 MHz clock; 5 seconds
APinMask long 0
p1 long 0
t1 long 0
t2 long 0
{{
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ TERMS OF USE: MIT License │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │
│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │
│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│
│is furnished to do so, subject to the following conditions: │
│ │
│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│
│ │
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │
│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │
│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
}}
Comments
In general, if the Spin code you have works correctly then you can just use that directly in the BASIC code by doing something like: (I haven't checked that code and don't have a device to actually test with, so it probably needs some tweaking, but that's the general idea.)
I was curious to see whether the actual Basic code would make the actual program size smaller than what I am getting when using the spin object code. The same experiment could be used to see what the code sizes look like with the dht22 C code, whenever that becomes available.
Since fastspin Basic is using LMM mode, it seems that all the program code has to be as small and compact as possible. I think that it would be easier to tweak Basic code then to tweak the spin objects, before using them in the fastspin Basic. Just my thoughts on this.
Ray
The "in the same way" is an interesting caveat though, because it may be that different languages make slightly different forms of code more "natural". In Spin, for example, it's pretty easy to interface to a PASM driver that runs in another COG, but that's harder in BASIC or C. In BASIC it's easier to use small inline functions than in the other languages. And C code tends not to use objects in the same way as Spin or BASIC.
I think there needs to be an explanation for why/when you would use the optimization option. I noticed that the dhtnn program, when you use 'no optimization', it runs as expected and the values are correct. But when you use 'Default Optimization', the program runs, but the values that you are printing are wrong, although there is quite a substantial reduction in program code size. I guess it would be some what difficult to find out where the problem lies when you do use the optimization option. Maybe a short summary on how the optimization thing works.
Ray
Still need to get a handle on how that Optimization works. With that info I guess you can start writing programs with that protocol in mind, I think. Also, what is the best way to have this program copied to EEPROM, so it starts up on power up?
The next step, I think, will have to be a pair up with a Raspberry Pi, that way I can access a program on the RasPi to get the temperature/humidity readings remotely.
Ray
Well, that's a bug then. Could you describe exactly how it's going wrong? (what kinds of values are being printed, etc.) As I said, I don't have any DHT hardware to test with, but if you could narrow down the problem to which function is producing the wrong result I may be able to figure out what's wrong.
There are a lot of optimizations that fastspin performs. The ones that most reduce the program size are unused method removal (getting rid of methods that are never called) and dead code removal (getting rid of parts of if statements that are always false). I think all of the optimizations in the "default optimization" level are performed within functions. At "full optimization" (-O2) it also does a few things like automatically inlining functions that are only called once.
No Optimization
Temperature : 73.04 *F Humidity : 45.100 %
Default Optimization
Temperature : 107.42 *F Humidity : 1.109E+08
Full Optimization
error status = 0
The values are so far apart, not sure what the heck is going on. But, when you compile with Full Optimization, the program code size is 7932 bytes. That is quite a reduction in program code size.
Ray
dgately
Another thing that I am noticing is the actual code itself. Of course it is a very simple program, but the fastspin Basic is just as readable as the C code, and vice-versa, in this instance. I know that C code can get very difficult to decipher at times.
I also looked at DHT11_Test.spin, which is similar in function, I sort looked at that code and was scratching my head thinking, what the heck. I am not saying this to start a language war, just my observation.
I am starting to like fastspin Basic, although I must confess, I have written some programs using FreeBasic, so it is starting to become very familiar.
Ray
I thought I would see something like 1.111 2.111 … But what I am seeing is 3F800000 40400000 … So, I must be using math.FFloat() incorrectly, right? I also wanted to see how the optimization would work on a program that had a Spin object attached.
Ray
FloatMath.spin
Secondly... a For loop would need some form of stepping of non-integers, if it allowed floating point as an index: Third... I'm not sure that you can print a floating value directly in this BASIC. You probably need a formatted print statement. I'm a bit fuzzy on this BASIC, but if it's like Spin, it will need a formatted print statement.
EDIT: Looks like FloatMath.spin uses an integer as basically a pointer to a 32 bit value that it manipulates as if a float. It packs a float into those 32 bits, that would need to be unpacked for use. i.e. you can't just print the integer, as-is.
dgately
For loops and print will work with floats, so you can do something like:
So there's no need for FloatMath.spin in a BASIC program, and it would actually be confusing to try to mix BASIC floating point and a Spin object.
Ray
I don't think it's a conflict -- you said things work if optimization is disabled, so you've probably encountered a bug in fastspin's optimizer. The Float*.spin files should work, it's just that calling them from BASIC requires some care since Spin does not have floating point as a built in type but BASIC does. (Plus there's not really any need to call them from BASIC, since BASIC has the same functions built in.)
I'd like to figure out what's going wrong, but I don't have DHTnn hardware to test with. Looking at the code I'd guess that if something's going wrong it may be with the data[] array in the DHTnn_Read function in DHTnn_Object.spin. You could try changing that from a local variable to a method variable (in the VAR block) to see if that helps.