fastspin Basic CM2302
Rsadeika
Posts: 3,837
in Propeller 1
What I am trying to do is create some fastspin Basic code to read a CM2302 temp/humid module.
In my fastspin basic code below, I am trying to get the correct hi/low sequence for the module to send something. I am not having very much success. I am not sure if I am interpreting the Basic output() = 1/0 command correctly. Or is there a better way to do this.
I also have the dht11/22 spin code below as reference material, that code is like Greek to me, but I am trying. Any assistance for solving this would be appreciated.
Thanks
Ray
My basic code
spin code
In my fastspin basic code below, I am trying to get the correct hi/low sequence for the module to send something. I am not having very much success. I am not sure if I am interpreting the Basic output() = 1/0 command correctly. Or is there a better way to do this.
I also have the dht11/22 spin code below as reference material, that code is like Greek to me, but I am trying. Any assistance for solving this would be appreciated.
Thanks
Ray
My basic code
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
spin code
''****************************************** ''* 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.