- Architect likes this
- Netduino Forums
- → Viewing Profile: Likes: JoopC
Community Stats
- Group Members
- Active Posts 148
- Profile Views 24171
- Member Title Advanced Member
- Age Age Unknown
- Birthday Birthday Unknown
-
Gender
Not Telling
User Tools
Latest Visitors
#64598 News from Secret Lab
Posted by JoopC on 02 December 2015 - 07:20 PM
#64145 Interrupts
Posted by JoopC on 19 September 2015 - 07:12 AM
Here is a good example snippet in VB (give you a idea) how to work with interrups AND READ() together, as Chris say, go as quickly as possible out of a intterupt, so make another thread. In my believe the on_interrupt occupy all the interrups in other threads until leave. This example works and tested.
COPY PASTE example in a new class sheet and see:
Imports System
Imports Microsoft.SPOT
'created by JoopC and Gé Brander
'there are many ideas implemented from other programmers.
'so credits to all of you.
#Region "Class, Electric board PulseMeter or TCRT5000 in a seperate Thread"
'change to TRUE to debug
#Const DEBUG = False
Namespace S0PulseMeter
Public Class PulseMeter
'energy consumption meter with S0 pulse output
Private _dPin As Cpu.Pin
Private _interruptPort As InterruptPort
Private _PulsesInKWH As Integer = 0
Private _intPulseCount As Integer = 0
Private _intWattNow As Integer = 0
Private _pulsesToWatt As Double = 0.0R
Private _PulsesToKW As Double = 0.0R
Private _PinOnWrite As strcPinOnWrite
Private _maximumCapacity As Integer
Private _dtStartTime As DateTime = DateTime.Now.AddHours(-1)
Private _returnToZeroDelayTicks As Long
Private _timeSpanZeroWattNow As Long = DateTime.Now.Ticks
Private _ID As Integer = -1
Private _unit As enumUnit
Private _resetToStartValueOnNewDay As Boolean
Private _startValue As Double
'new
Private _TotalPulses As Integer
'watermeter support
Private _isTCRT5000 As Boolean
Private _receivePulse As Boolean
Private PulseThread As Thread = Nothing
Public Sub Start()
PulseThread.Start()
End Sub
Public Sub New()
Me._PulsesInKWH = 0
Me._pulsesToWatt = 0
Me._PulsesToKW = 0
End Sub
Public Sub New(
ByVal dPin As Cpu.Pin,
ByVal Pulses As Integer,
ByVal PinOnWrite As strcPinOnWrite,
ByVal capacity As Integer,
ByVal returnToZeroDelayTicks As Long,
ByVal ID As Integer,
ByVal unit As enumUnit,
ByVal resetToStartValueOnNewDay As Boolean,
ByVal StartValue As Double,
ByVal TCRT5000 As Boolean)
PulseThread = New Thread(AddressOf InterruptPortThread)
Me._PulsesInKWH = CInt((3600 / Pulses) * 1000)
Me._pulsesToWatt = (1000 / Pulses)
Me._PulsesToKW = Pulses
Me._PinOnWrite = PinOnWrite
Me._dPin = dPin
Me._maximumCapacity = capacity
Me._returnToZeroDelayTicks = returnToZeroDelayTicks
Me._ID = ID
Me._unit = unit
Me._resetToStartValueOnNewDay = resetToStartValueOnNewDay
Me._startValue = StartValue
Me._isTCRT5000 = TCRT5000
End Sub
''' <summary>
'''
''' </summary>
''' <param name="data1"></param>
''' <param name="data2"></param>
''' <param name="time"></param>
''' <remarks></remarks>
Private Sub interruptTCRT5000(data1 As UInteger, data2 As UInteger, time As Date)
Me._interruptPort.DisableInterrupt()
Me._receivePulse = True
#If DEBUG Then
Debug.Print("Pulse Detected " & DateTime.Now.ToString)
#End If
Thread.Sleep(10)
End Sub
Private Sub InterruptPortThread()
Me._interruptPort = New InterruptPort(Me._dPin, False, Port.ResistorMode.PullDown, Port.InterruptMode.InterruptEdgeHigh)
If Me._isTCRT5000 Then
'watermeter
AddHandler Me._interruptPort.OnInterrupt, AddressOf interruptTCRT5000
'max time to wait if a pulse is received
Const cnstWAITtime As Integer = 1800
Do While True
If Me._receivePulse AndAlso Not blnExclusiveAction AndAlso Me._interruptPort.Read() Then
#If DEBUG Then
Debug.Print("TCRT5000 Pulse written & reset interrupt " & DateTime.Now.ToString)
#End If
functions.WritePin(Me._PinOnWrite, True)
Me._timeSpanZeroWattNow = DateTime.Now.Ticks + Me._returnToZeroDelayTicks
Me._intPulseCount += 1
Me._intWattNow = CInt(Me._PulsesInKWH / ((DateTime.Now.Ticks - Me._dtStartTime.Ticks) / TimeSpan.TicksPerSecond))
Me._dtStartTime = DateTime.Now
Thread.Sleep(40)
functions.WritePin(Me._PinOnWrite, False)
'reset pulse
Me._receivePulse = False
Me._interruptPort.EnableInterrupt()
Me._interruptPort.ClearInterrupt()
End If
#If DEBUG Then
Debug.Print("Sleep " & DateTime.Now.ToString)
#End If
Thread.Sleep(cnstWAITtime)
Loop
Else
AddHandler Me._interruptPort.OnInterrupt, AddressOf interrupt_soPulseMeter
End If
End Sub
''' <summary>
'''
''' </summary>
''' <param name="data1"></param>
''' <param name="data2"></param>
''' <param name="time"></param>
''' <remarks></remarks>
Private Sub interrupt_soPulseMeter(data1 As UInteger, data2 As UInteger, time As Date)
' * To prevent stray voltage pulses *
'' ******* ******
'' * If your system has a higher standby power usage than 10W *
'' * adjust the cnstMinWattageToDetect as needed *
Const cnstMinWattageToDetect As Integer = 10
Try
functions.WritePin(Me._PinOnWrite, True)
Dim intWattage As Integer = CInt(Me._PulsesInKWH / ((DateTime.Now.Ticks - Me._dtStartTime.Ticks) / TimeSpan.TicksPerSecond))
If intWattage > cnstMinWattageToDetect AndAlso intWattage < Me._maximumCapacity Then
Me._timeSpanZeroWattNow = DateTime.Now.Ticks + Me._returnToZeroDelayTicks
Me._intPulseCount += 1
Me._intWattNow = intWattage
End If
_dtStartTime = DateTime.Now
Thread.Sleep(40)
functions.WritePin(Me._PinOnWrite, False)
Me._interruptPort.ClearInterrupt()
If blnExclusiveAction Then
Me._interruptPort.DisableInterrupt()
SyncLock dummySPIsyncLock
End SyncLock
Me._interruptPort.EnableInterrupt()
End If
Catch ex As Exception
End Try
End Sub
Public Sub clear()
Me._intPulseCount = 0
End Sub
Public ReadOnly Property ID As Integer
Get
Return Me._ID
End Get
End Property
Public ReadOnly Property resetToStartValueOnNewDay As Boolean
Get
Return Me._resetToStartValueOnNewDay
End Get
End Property
Public ReadOnly Property StartValue As Double
Get
Return Me._startValue
End Get
End Property
Public ReadOnly Property unit As enumUnit
Get
Return Me._unit
End Get
End Property
Public Property Pulses As Integer
Set(ByVal value As Integer)
Me._intPulseCount = If(value < 0, 0, value)
End Set
Get
Return Me._intPulseCount
End Get
End Property
Public Property WattageNow As Integer
Set(ByVal value As Integer)
Me._intWattNow = If(value < 0, 0, value)
End Set
Get
If DateTime.Now.Ticks > Me._timeSpanZeroWattNow Then
Me._intWattNow = 0
End If
Return Me._intWattNow
End Get
End Property
Public Property KWH As Double
Set(ByVal value As Double)
Me._intPulseCount = If(value < 0, 0, Integer.Parse((Me._PulsesToKW * value).ToString("F0")))
End Set
Get
Return Me._intPulseCount * Me._pulsesToWatt
End Get
End Property
Public Property totalPulses As Integer
Set(ByVal value As Integer)
Me._TotalPulses = value
End Set
Get
Return Me._TotalPulses
End Get
End Property
Public Property totalKWH As Double
Set(ByVal value As Double)
Me._TotalPulses = If(value < 0, 0, Integer.Parse((Me._PulsesToKW * value).ToString("F0")))
End Set
Get
Return Me._TotalPulses * Me._pulsesToWatt
End Get
End Property
Public Sub resetZeroDelay()
Me._timeSpanZeroWattNow = DateTime.Now.Ticks + Me._returnToZeroDelayTicks
End Sub
End Class
End Namespace
#End Region
- WriterGuy likes this
#61002 Data Logging and much more with a Netduino Plus 2
Posted by JoopC on 17 December 2014 - 01:07 PM
Are you not so handy?,...It is very simple to start with the pre-fab board from sateetje, it only cost a few dollars.
all together:
You can see all your output on your Phone App:
Most of the settings can set default OR:
You can program you Phone app and Netduino dynamicly with the added web pages special designed for the Netduino.
WITHOUT any knowledge of programming!, just deploy the HEX file (or program with VS Express) to your Netduino will do.
With a add-in Help on every level, a hobby must be fun.
You can send pre-defined commando's over the Internet to your netduino to listen or to do (switches etc);
(you can even program the content of the Phone buttons with your own language/usage)
To connect the hardware, there are tons of images included to help you.
Or go to the forum and ask, in any language (we have Google Translate )
There is to many to play with in this program. When we display all the possibilities than Chris Walker wil get angry .
- Chris Walker and Automate like this
#60178 Windows on Devices? When?
Posted by JoopC on 22 September 2014 - 08:13 AM
My contribution was to tease Microsoft with a wink to the court system, like beastyboy explained.
.......
Having said that..... In the Netherlands we are asking ourselves if it was not wiser for Microsoft to put more effort in the Netduino project. The Netduino is as stable as a rock and compared to the Intel, light years ahead. But about choices and Microsoft.....
- Yourhit and Jamesthype like this
#59229 Windows on Devices? When?
Posted by JoopC on 15 July 2014 - 07:12 AM
IoT and .....Intel Galileo, as far I can see based on C++,..... come on...are they for real? (I have seen the video of Steve Teixeira) Then I must do 4 steps back, and I have seen "Hello World" already. There is realy nothing that I can not make with a Netduino Plus 2. and last but not least, Visual Basic and C#...so easy to use in comparison with C++.
About a overwhelmed response, yes when you give the devices for free....
Even with the little memory of the Netduino (that is the only bottleneck for me) I can win every contest with 2 fingers in my nose. And I am not a prof.
- Juzzer likes this
#56574 Visual Basic in action. dataLogging the easy way with your Netduino
Posted by JoopC on 02 March 2014 - 09:09 AM
for the Visual Basic enthusiasts, like me and the family.
We have written a very large program in Visual Basic for the Netduino dataLogging the easy way, which can handle the most of your VB questions on HowTo... The whole code can simple re-programmed (as we say) with the attached webpages. Yes,.... you can.
download the source code and the web pages from CodePlex:
- Stari likes this
#56218 Is data lost when UDP socket receive buffer is too small?
Posted by JoopC on 20 February 2014 - 07:37 AM
- Paul Newton likes this
#56140 Socket
Posted by JoopC on 17 February 2014 - 09:27 AM
You program it wrong, you'll have to do it like this as example:
Const cnstHostThingSpeak As String = "thingspeak.com"
Dim IPEndPoint As New IPEndPoint(Dns.GetHostEntry(cnstHostThingSpeak).AddressList(0), 80)
- Stari likes this
#54570 Long live the Netduino
Posted by JoopC on 09 December 2013 - 02:53 PM
#51044 I2C LCD Digole in Visual Basic
Posted by JoopC on 03 July 2013 - 09:10 AM
I have rewritten the Arduino program code for the Digole I2C backpack in visual basic.
The backpack supported Serial, I2C and SPI all in one for a few dollar.
Datasheet: http://www.digole.co...pter-Manual.pdf
Convert program code from Visual Basic to C# link: http://www.developer...t/csharp-to-vb/
The I2C driver is translated from NooM's PlayGround http://forums.netdui...ge-1#entry41558
Here is the Backpack, you can solder the backpack on LCDs who has assembled without a backpack.
http://www.ebay.com/...=item3a80983190
You can use it with a 16x2 or 16x4 or 20x2 or 20x4 or 40x2 or 40x4 lcd display
Example: this is ready with backpack 20x4
http://www.ebay.com/...=item3a823f4585
or real hippiediedip, :
http://www.ebay.com/...=item3a7b862984
EDIT: Now all the program code is ported, in the attached file.
Attached Files
- Module1.vb 23.36KB 16 downloads
- NooM likes this
#50653 4X20 LCD display with I2C Interface
Posted by JoopC on 20 June 2013 - 10:03 AM
I have convert the LCD class to Visual Basic and make some changes i found
required for correct functioning. Just copy paste the Class in your program (I tested it well)
so now in de code:
dim Display As VBLiquidCrystal_I2C = New VBLiquidCrystal_I2C(lcdAddress, col, row)
edit: 21-06-2013 I have made some modifications in de class.
Public Class VBLiquidCrystal_I2C Private Const cnstClearDisplay As Integer = &H1 Private Const cnstReturnHome As Integer = &H2 Private Const cnstEntryModeSet As Integer = &H4 Private Const cnstDisplayControl As Integer = &H8 Private Const cnstCursorShift As Integer = &H10 Private Const cnstFunctionSet As Integer = &H20 Private Const cnstSETCGRAMADDR As Integer = &H40 Private Const cnstSETDDRAMADDR As Integer = &H80 Private Const cnstEntryRight As Integer = &H0 Private Const cnstEntryLeft As Integer = &H2 Private Const cnstEntryShiftIncrement As Integer = &H1 Private Const cnstEntryShiftDecrement As Integer = &H0 Private Const cnstDisplayOn As Integer = &H4 Private Const cnstDisplayOff As Integer = &H0 Private Const cnstCursorOn As Integer = &H2 Private Const cnstCursorOff As Integer = &H0 Private Const cnstBlinkOn As Integer = &H1 Private Const cnstBlonkOff As Integer = &H0 Private Const cnstDisplayMove As Integer = &H8 Private Const cnstCursorMove As Integer = &H0 Private Const cnstMoveRight As Integer = &H4 Private Const cnstMoverLeft As Integer = &H0 Private Const cnst8BitMode As Integer = &H10 Private Const cnst4BitMode As Integer = &H0 Private Const cnst2Line As Integer = &H8 Private Const cnst1Line As Integer = &H0 Private Const cnst5x10Dots As Integer = &H4 Private Const cnst5x8Dots As Integer = &H0 Private Const cnstBackLight As Integer = &H8 Private Const cnstNoBackLight As Integer = &H0 Private Const cnstMaxFreq As Integer = 100 Private _Addr As Byte Private _displayfunction As Byte Private _displaycontrol As Byte Private _displaymode As Byte Private _numlines As Byte Private _cols As Byte Private _rows As Byte Private _backlightval As Byte Private _I2C As MultiI2C ' When the display powers up, it is configured as follows: ' ' 1. Display clear ' 2. Function set: ' DL = 1; 8-bit interface data ' N = 0; 1-line display ' F = 0; 5x8 dot character font ' 3. Display on/off control: ' D = 0; Display off ' C = 0; Cursor off ' B = 0; Blinking off ' 4. Entry mode set: ' I/D = 1; Increment by 1 ' S = 0; No shift ' ' Note, however, that resetting the Arduino doesn't reset the LCD, so we ' can't assume that its in that state when a sketch starts (and the ' LiquidCrystal constructor is called).#Region "Constructors and Initializers" Public Sub New(ByVal lcd_Addr As Byte, ByVal lcd_cols As Byte, ByVal lcd_rows As Byte) Me._Addr = lcd_Addr Me._cols = lcd_cols Me._rows = lcd_rows Me._backlightval = cnstNoBackLight Me._I2C = New MultiI2C(lcd_Addr, cnstMaxFreq) Me.begin(Me._cols, Me._rows) End Sub Public Sub reset() Me.begin(Me._cols, Me._rows) End Sub ''' <summary> ''' Sends the initialization sequence for the device ''' </summary> ''' <param name="cols">Number of columns</param> ''' <param name="lines">Number of rows</param> ''' <param name="dotsize">Optional cursor size</param> Public Sub begin(ByVal cols As Byte, ByVal lines As Byte, Optional dotsize As Byte = cnst5x8Dots) Me._displayfunction = cnst4BitMode Or cnst1Line Or cnst5x8Dots If lines > 1 Then Me._displayfunction = CByte(Me._displayfunction Or cnst2Line) End If Me._numlines = lines ' for some 1 line displays you can select a 10 pixel high font If (dotsize <> 0) AndAlso (lines = 1) Then Me._displayfunction = CByte(Me._displayfunction Or cnst5x10Dots) End If ' SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! ' according to datasheet, we need at least 40ms after power rises above 2.7V ' before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 'Thread.Sleep(50); ' Now we pull both RS and R/W low to begin commands Me.expanderWrite(Me._backlightval) ' reset expander and turn backlight off (Bit 8 =1) Thread.Sleep(1000) 'put the LCD into 4 bit mode ' this is according to the hitachi HD44780 datasheet ' figure 24, pg 46 'UShort * 2^8 for left shifting 8 bit (<< 8) ' we start in 8bit mode, try to set 4 bit mode Me.write4bits(CByte(&H3 * 2 ^ 4)) Thread.Sleep(8) ' wait min 4.1ms ' second try Me.write4bits(CByte(&H3 * 2 ^ 4)) Thread.Sleep(8) ' wait min 4.1ms ' third go! Me.write4bits(CByte(&H3 * 2 ^ 4)) Thread.Sleep(4) ' finally, set to 4-bit interface Me.write4bits(CByte(&H2 * 2 ^ 4)) ' set # lines, font size, etc. Me.command(CByte(cnstFunctionSet Or Me._displayfunction)) ' turn the display on with no cursor or blinking default Me._displaycontrol = cnstDisplayOn Or cnstCursorOff Or cnstBlonkOff Me.display() ' clear it off Me.clear() ' Initialize to default text direction (for roman languages) Me._displaymode = cnstEntryLeft Or cnstEntryShiftDecrement ' set the entry mode Me.command(CByte(cnstEntryModeSet Or Me._displaymode)) Me.home() End Sub#End Region#Region "High Level me.commands" Public Sub clear() Me.command(cnstClearDisplay) Thread.Sleep(2) End Sub Public Sub home() Me.command(cnstReturnHome) Thread.Sleep(2) End Sub Public Sub noDisplay() Dim display As Byte = cnstDisplayOn Me._displaycontrol = Me._displaycontrol And CByte(Not display) Me.command(CByte(cnstDisplayControl Or Me._displaycontrol)) End Sub Public Sub display() Me._displaycontrol = CByte(_displaycontrol Or cnstDisplayOn) Me.command(CByte(cnstDisplayControl Or Me._displaycontrol)) End Sub Public Sub noBlink() Dim blink As Byte = cnstBlinkOn Me._displaycontrol = Me._displaycontrol And CByte(Not blink) Me.command(CByte(cnstDisplayControl Or Me._displaycontrol)) End Sub Public Sub blink() Me._displaycontrol = CByte(Me._displaycontrol Or cnstBlinkOn) Me.command(CByte(cnstDisplayControl Or Me._displaycontrol)) End Sub Public Sub noCursor() Dim cursor As Byte = cnstCursorOn Me._displaycontrol = Me._displaycontrol And CByte(Not cursor) Me.command(CByte(cnstDisplayControl Or Me._displaycontrol)) End Sub Public Sub cursor() Me._displaycontrol = CByte(Me._displaycontrol Or cnstCursorOn) Me.command(CByte(cnstDisplayControl Or Me._displaycontrol)) End Sub Public Sub scrollDisplayLeft() Me.command(cnstCursorShift Or cnstDisplayMove Or cnstMoverLeft) End Sub Public Sub scrollDisplayRight() Me.command(cnstCursorShift Or cnstDisplayMove Or cnstMoveRight) End Sub Public Sub leftToRight() Me._displaymode = CByte(Me._displaymode Or cnstEntryLeft) Me.command(CByte(cnstEntryModeSet Or Me._displaymode)) End Sub Public Sub rightToLeft() Dim entry As Byte = cnstEntryLeft Me._displaymode = _displaymode And CByte(Not entry) Me.command(CByte(cnstEntryModeSet Or Me._displaymode)) End Sub Public Sub noBacklight() Me._backlightval = cnstNoBackLight expanderWrite(0) End Sub Public Sub backlight() Me._backlightval = cnstBackLight expanderWrite(0) End Sub Public Sub autoscroll() Me._displaymode = CByte(Me._displaymode Or cnstEntryShiftIncrement) Me.command(CByte(cnstEntryModeSet Or Me._displaymode)) End Sub Public Sub noAutoscroll() Dim entry As Byte = cnstEntryShiftIncrement Me._displaymode = Me._displaymode And CByte(Not entry) Me.command(CByte(cnstEntryModeSet Or Me._displaymode)) End Sub#End Region#Region "Charaters and writing" ''' <summary> ''' Allows us to fill the first 8 CGRAM locations with custom characters ''' </summary> ''' <param name="location"> 0-7 locations</param> ''' <param name="charmap"> byte[8] containg charater map</param> Public Sub createChar(ByVal location As Byte, ByVal charmap As Byte()) 'SyncLock LCDLock location = CByte(location And &H7) ' we only have 8 locations 0-7 Me.command(CByte(cnstSETCGRAMADDR Or (CLng(location * 2 ^ 3)))) For i As Integer = 0 To 7 Me.write(charmap(i)) Next 'End SyncLock End Sub Public Sub setCursor(ByVal col As Byte, ByVal row As Byte) Dim row_offsets As Integer() = {&H0, &H40, &H14, &H54} If row > Me._numlines Then ' we count rows starting w/0 row = CByte(Me._numlines - 1) End If Me.command(CByte(cnstSETDDRAMADDR Or (col + row_offsets(row)))) End Sub Public Sub write(ByVal value As Byte()) For position As Integer = 0 To value.Length - 1 Me.write(value(position)) Next End Sub Public Sub write(ByVal value As String) Dim writeVal As Byte() = Encoding.UTF8.GetBytes(value) For position As Integer = 0 To value.Length - 1 Me.write(writeVal(position)) Next End Sub Public Sub write(ByVal value As Byte) Me.send(value, &H1) End Sub Public ReadOnly Property Rows As Integer Get Return Me._rows End Get End Property Public ReadOnly Property Cols As Integer Get Return Me._cols End Get End Property#End Region#Region "Mid level commands" '********** mid level commands, for sending data/cmds Private Sub command(ByVal value As Byte, ByVal vv As Byte) Me.send(value, vv) End Sub Private Sub command(ByVal value As Byte) Me.send(value, 0) End Sub '''/compatibility API function aliases Public Sub blink_on() Me.blink() End Sub Public Sub blink_off() Me.noBlink() End Sub Public Sub cursor_on() Me.cursor() End Sub Public Sub cursor_off() Me.noCursor() End Sub Public Sub setBacklight(ByVal new_val As Boolean) If new_val Then Me.backlight() Else Me.noBacklight() End If End Sub Public Sub load_custom_character(ByVal char_num As Byte, ByVal charMap As Byte()) Me.createChar(char_num, charMap) End Sub#End Region#Region "Low Level commands" ''' <summary> ''' write either command or data to the devide ''' </summary> ''' <param name="value">byte to send</param> ''' <param name="mode">0 = command, 1 = data</param> Private Sub send(ByVal value As Byte, ByVal mode As Byte) Dim highnib As Byte = CByte(value And &HF0) Dim lownib As Byte = CByte((CLng(value * 2 ^ 4)) And &HF0) Me.write4bits(CByte((highnib) Or mode)) Me.write4bits(CByte((lownib) Or mode)) End Sub Private Sub write4bits(ByVal value As Byte) expanderWrite(value) pulseEnable(value) End Sub ''' <summary> ''' Lowest level command to send data to the I2C port ''' </summary> ''' <param name="_data"></param> Private Sub expanderWrite(ByVal data As Byte) Me._I2C.Write(New Byte() {CByte(data Or Me._backlightval)}) End Sub Private Sub pulseEnable(ByVal data As Byte) expanderWrite(CByte(data Or &H4)) ' En high 'Thread.Sleep(1); // enable pulse must be >450ns Me.expanderWrite(CByte(data And Not &H4)) ' En low 'Thread.Sleep(50); // commands need > 37us to settle End Sub#End RegionEnd Class
- carb likes this
- Netduino Forums
- → Viewing Profile: Likes: JoopC
- Privacy Policy