B4R Question Button selection via A0 on ESP8266

Discussion in 'B4R Questions' started by bdunkleysmith, Dec 19, 2017.

  1. bdunkleysmith

    bdunkleysmith Member Licensed User

    I am trying to implement a user interface by way of a 2X16 LCD display and an analog 5 button input.

    The extract of code below obtains the SSIDs of available networks and displays the initial one from the wfi.scan array on the second line of the LCD display. The intent of the remainder of the code is to loop while scanning the button input via pin A0. Selecting the DOWN button should replace the SSID display with the next in the array and selecting the UP arrow should move the selection back through the array. The loop should continue until the SELECT button is pressed, at which point the SSID will be saved and the sub exited.

    Code:
    Sub Process_Globals
        
    Public Serial1 As Serial
        
    Private usocket As WiFiUDP
        
    Private wifi As ESP8266WiFi
        
    Private ip(4As Byte
        
    Private port As UInt = 3661
        
    Private astream As AsyncStreams
        
    Private bc As ByteConverter
        
    Private lcd As LiquidCrystal
        
    Private TextLine(5As String = Array As String (" ABCDEFGHIJKLMNO""PQRSTUVWXYZ01234""56789abcdefghijk""lmnopqrstuvwxyz?""!@#$%^&*()+-=_/ ")
        
    Private LineNo As UInt = 0
        
    Private PIN_A0 As Pin
        
    Private btnValues() As UInt = Array As UInt(502504506508501025' RIGHT, UP, DOWN, LEFT, SELECT, NO BUTTON
        Private btn As UInt
    End Sub

    Sub ScanNetworks
        lcd.Write(
    "Scan for network")
        
    Dim numberOfNetworks As Byte = wifi.Scan
        
    Dim reading As UInt = PIN_A0.AnalogRead
        
    For j = 0 To btnValues.Length - 1
            
    If reading < btnValues(j) Then
                btn = j
                
    Log("Read button initially: ",btn)
                
    Exit
            
    End If
        
    Next

        
    Dim i As UInt = 0
        
        
    Do Until btn = 4 'SELECT
            lcd.Clear
            lcd.Write(
    "Select network")
            lcd.SetCursor(
    01)
            lcd.Write(wifi.ScannedSSID(i))   
            reading = PIN_A0.AnalogRead
            
    For j = 0 To btnValues.Length - 1
                
    If reading < btnValues(j) Then
                    btn = j
                    
    Exit
                
    End If
            
    Next
            
    If btn = 1 And i > 0 Then 'UP
                i = i - 1
            
    End If
            
    If btn = 2 And i < numberOfNetworks - 2 Then 'DOWN
                i = i + 1
            
    End If
        
    Loop
        GlobalStore.Put(
    0, wifi.ScannedSSID(i))
    End Sub
    However after displaying Select network then the initial SSID on the second row of the LCD display, the code crashes with an Exception (28): error message.

    It seems that after executing the loop perhaps a dozen times the crash occurs. I suspect a memory issue, but I don't know how to diagnose that.

    Should my code achieve the desired user interface or is there a coding flaw?
     
  2. Erel

    Erel Administrator Staff Member Licensed User

  3. bdunkleysmith

    bdunkleysmith Member Licensed User

    My initial code was based on that example and used AddLooper, but it too failed and that's why I tried incorporating the button press detection code directly into the sub.

    A simple example of reading the analog buttons using the following code works and so proves my hardware is OK:

    Code:
    Private Sub AppStart
        Serial1.Initialize(
    115200)
        lcd.Initialize(
    162555Array As Byte (12131415))
        lcd.Begin(
    162)
        PIN_A0.Initialize(PIN_A0.A0, PIN_A0.MODE_INPUT)
        AddLooper(
    "ReadButton")
    End Sub

    Sub ReadButton
        
    Dim reading As UInt = PIN_A0.AnalogRead
        
    For i = 0 To btnValues.Length - 1
            
    If reading < btnValues(i) Then
                btn = i
                lcd.Clear
                lcd.Write(
    "Select network")
                lcd.SetCursor(
    01)
                lcd.Write(btn)
                
    Exit
            
    End If
        
    Next
    End Sub
    The LCD display updates only when one of the 5 buttons is pressed, with the second line of the LCD display updating to show the number associated with the button which was pressed.

    Changing the code in the original post to use the ReadButton AddLooper also results in failure:

    Code:
    rivate Sub AppStart
        Serial1.Initialize(
    115200)
        
    Log("AppStart")
        lcd.Initialize(
    162555Array As Byte (12131415))
        lcd.Begin(
    162)
        PIN_A0.Initialize(PIN_A0.A0, PIN_A0.MODE_INPUT)
        AddLooper(
    "ReadButton")
        ScanNetworks
        
    End If
    End Sub

    Sub ScanNetworks
        lcd.Clear
        lcd.Write(
    "Scan for network")
        
    Dim numberOfNetworks As Byte = wifi.Scan
        lcd.Clear
        lcd.Write(
    "Select network")
        lcd.SetCursor(
    01)
        
    Dim i As UInt = 0
        lcd.Write(wifi.ScannedSSID(i))
        
    Do Until btn = 4 'SELECT
            If btn = 1 And i > 0 Then 'UP
                i = i - 1
                lcd.Clear
                lcd.Write(
    "Select network")
                lcd.SetCursor(
    01)
                lcd.Write(wifi.ScannedSSID(i))
            
    End If
            
    If btn = 2 And i < numberOfNetworks - 2 Then 'DOWN
                i = i + 1
                lcd.Clear
                lcd.Write(
    "Select network")
                lcd.SetCursor(
    01)
                lcd.Write(wifi.ScannedSSID(i))
            
    End If
        
    Loop
        GlobalStore.Put(
    0, wifi.ScannedSSID(i))
    End Sub

    Sub ReadButton
        
    Dim reading As UInt = PIN_A0.AnalogRead
        
    For i = 0 To btnValues.Length - 1
            
    If reading < btnValues(i) Then
                btn = i
                
    Exit
            
    End If
        
    Next
    End Sub
    As mentioned in the first post, the intent of the code is to be able to use the UP and DOWN buttons to move display of the SSID through the array resulting from wifi. Then when the desired SSID is displayed, pushing the SELECT button will save the SSID and move on with execution of more code which will consume that saved SSID.

    It seems it is not possible to wait in a loop until the SELECT button is pressed, in the meantime executing code if the UP or DOWN button press is detected. Will Do Until btn = 4 never work? Perhaps Arduino is not the right platform for this project.

    STOP PRESS:

    Removed ReadButton as an AddLooper and moved ReadButton call to ScanNetworks sub. Also added Delay(10) in Do Until loop:

    Code:
    rivate Sub AppStart
        Serial1.Initialize(
    115200)
        
    Log("AppStart")
        lcd.Initialize(
    162555Array As Byte (12131415))
        lcd.Begin(
    162)
        PIN_A0.Initialize(PIN_A0.A0, PIN_A0.MODE_INPUT)
        ScanNetworks
        
    End If
    End Sub

    Sub ScanNetworks
        lcd.Clear
        lcd.Write(
    "Scan for network")
        
    Dim numberOfNetworks As Byte = wifi.Scan
        lcd.Clear
        lcd.Write(
    "Select network")
        lcd.SetCursor(
    01)
        
    Dim i As UInt = 0
        lcd.Write(wifi.ScannedSSID(i))
        
    Do Until btn = 4 'SELECT
            ReadButton
            
    If btn = 1 And i > 0 Then 'UP
                i = i - 1
                lcd.Clear
                lcd.Write(
    "Select network")
                lcd.SetCursor(
    01)
                lcd.Write(wifi.ScannedSSID(i))
            
    End If
            
    If btn = 2 And i < numberOfNetworks - 2 Then 'DOWN
                i = i + 1
                lcd.Clear
                lcd.Write(
    "Select network")
                lcd.SetCursor(
    01)
                lcd.Write(wifi.ScannedSSID(i))
            
    End If
            Delay(
    10)
        
    Loop
        GlobalStore.Put(
    0, wifi.ScannedSSID(i))
    End Sub

    Sub ReadButton
        
    Dim reading As UInt = PIN_A0.AnalogRead
        
    For i = 0 To btnValues.Length - 1
            
    If reading < btnValues(i) Then
                btn = i
                
    Exit
            
    End If
        
    Next
    End Sub
    This is partly functioning, in that the WDT is not forcing a reset immediately the code is run, which is probably because I've introduced the delay. However ReadButton is called so quickly a single press of the UP or DOWN button is read in as multiple presses and so the display does not just step through the list one at a time.

    So progress is being made slowly . . .
     
    Last edited: Dec 19, 2017
  4. Erel

    Erel Administrator Staff Member Licensed User

    That's true. It is also not needed.

    Update the lcd in ReadButton instead.
     
  5. bdunkleysmith

    bdunkleysmith Member Licensed User

    Erel I updated my post #3 before I saw your comments.

    The problem I see in updating the LCD in ReadButton is that I need the array data from wifi.scan and that seems problematic given my understanding of variables in B4R.

    But I'm encouraged to continue thanks to your great input as usual.
     
  6. Erel

    Erel Administrator Staff Member Licensed User

    wifi.ScannedSSID will be available everywhere.

    If it is a local array then you can use GlobalStore to store its values.
     
  7. bdunkleysmith

    bdunkleysmith Member Licensed User

    I now have fully functioning code which allows the 2X16 LCD keypad shield to provide a user interface for the WiFi shield. I have shown the code below in full in case it is of any use to others. It may not be the best coding, but it works and I've found the Delay(100) introduced into the keypad scanning loop is critical.

    The code:
    * A scan is made of available WiFi networks
    * Allows the user to move through the list of available networks using the UP/DOWN buttons and then the desired one is chosen using the SELECT button
    * If a file named with the network SSID (which contains the SSID password) has been previously saved, the user interface allows the user to use the saved password or enter a new one via the LEFT or RIGHT buttons
    * If the user selects to use the existing saved password it is retrieved from the saved file and the WiFi shield logs onto the chosen network
    * If the user wants to enter a new password or there is no existing file for the chosen network, a password can be entered by using UP/DOWN/LEFT/RIGHT buttons to select the characters from those displayed. Each is entered via the SELECT button and a BACKSPACE function is achieved with the LEFT button if the cursor is in the first column. When all characters have been selected, selecting the space character at the start of the first line will save the password to a filed named with the chosen SSID. Again the WiFi then logs onto the chosen network.
    * For my application once the logon is successful it broadcasts data received via the serial port to the IP broadcast address determined from the subnet.

    Code:
    Sub Process_Globals
        
    Public Serial1 As Serial
        
    Private usocket As WiFiUDP
        
    Private wifi As ESP8266WiFi
        
    Private fs As ESP8266FileSystem
        
    Private ip(4As Byte
        
    Private port As UInt = 3661
        
    Private astream As AsyncStreams
        
    Private bc As ByteConverter
        
    Private lcd As LiquidCrystal
        
    Private TextLine(5As String = Array As String (" ABCDEFGHIJKLMNO""PQRSTUVWXYZ01234""56789abcdefghijk""lmnopqrstuvwxyz?""!@#$%^&*()+-=_/ ")
        
    Private PIN_A0 As Pin
        
    Private btnValues() As UInt = Array As UInt(502504506508501025' RIGHT, UP, DOWN, LEFT, SELECT, NO BUTTON
        Private btn As UInt
    End Sub

    Private Sub AppStart
        Serial1.Initialize(
    115200)
        lcd.Initialize(
    162555Array As Byte (12131415))
        lcd.Begin(
    162)
        PIN_A0.Initialize(PIN_A0.A0, PIN_A0.MODE_INPUT)
        
    If(fs.initialize) Then
            
    Log("File system successfully initialized")
        
    Else
            
    Log("Could not initialize filesystem")
        
    End If
        SelectNetwork
        CheckForPassword
        ConnectToNetwork
        ListFiles
    End Sub

    Sub astream_NewData (Buffer() As Byte)
        usocket.BeginPacket(ip, port)
        
    For Each s() As Byte In bc.Split(Buffer, CRLF)
            
    If s.Length <> 0 Then
                usocket.Write(s)
                usocket.SendPacket
                
    If bc.StringFromBytes(bc.SubString2((s),01)) = "T" Then
                    lcd.Clear
                    lcd.SetCursor(
    0,0)
                    lcd.Write(bc.StringFromBytes(bc.SubString2((s),
    1,6)))
                
    End If
                
    If bc.StringFromBytes(bc.SubString2((s),01)) = "S" Then
                    lcd.SetCursor(
    6,0)
                    lcd.Write(bc.StringFromBytes(bc.SubString2((s),
    1,11)))
                
    End If
                
    If bc.StringFromBytes(bc.SubString2((s),01)) = "H" Then
                    lcd.setcursor(
    01)
                    lcd.Write(bc.StringFromBytes(bc.SubString2((s),
    1,3)))
                
    End If
                
    If bc.StringFromBytes(bc.SubString2((s),01)) = "F" Then
                    lcd.setcursor(
    5,1)
                    lcd.Write(bc.StringFromBytes(bc.SubString2((s),
    1,10)))
                
    End If
            
    End If
        
    Next
    End Sub

    Sub usocket_PacketArrived (Data() As Byte, ip1() As Byte, port1 As UInt)
        
    'not used
        Log("Packet arrived")
    End Sub

    Sub astream_Error
        
    Log("Error")
    End Sub

    Sub SelectNetwork    'List all SSIDs & select desired one via LCD UP/DOWN/SELECT buttons
        Dim i As UInt = 0
        btn = 
    5 'Reset button value
        lcd.Clear
        lcd.Write(
    "Scan for network")
        
    Dim numberOfNetworks As Byte = wifi.Scan
        
    Log("Found ", numberOfNetworks, " networks")
        
    For c = 0 To numberOfNetworks - 1
            
    Log("SSID: ", wifi.ScannedSSID(c), "|RSSI: ", wifi.ScannedRSSI(c))
        
    Next
        lcd.Clear
        lcd.Write(
    "Select SSID ")
        lcd.Write(
    "1/")
        lcd.Write(numberOfNetworks)
        lcd.SetCursor(
    01)
        lcd.Write(wifi.ScannedSSID(i))
        
    Do Until btn = 4 'SELECT
            Dim reading As UInt = PIN_A0.AnalogRead
            
    For j = 0 To btnValues.Length - 1
                
    If reading < btnValues(j) Then
                    btn = j
                    
    Exit
                
    End If
            
    Next
            
    If btn = 1 And i > 0 Then 'UP
                i = i - 1
                lcd.Clear
                lcd.Write(
    "Select SSID ")
                lcd.Write(i + 
    1)
                lcd.Write(
    "/")
                lcd.Write(numberOfNetworks)
                lcd.SetCursor(
    01)
                lcd.Write(wifi.ScannedSSID(i))
            
    End If
            
    If btn = 2 And i < numberOfNetworks - 1 Then 'DOWN
                i = i + 1
                lcd.Clear
                lcd.Write(
    "Select SSID ")
                lcd.Write(i + 
    1)
                lcd.Write(
    "/")
                lcd.Write(numberOfNetworks)
                lcd.SetCursor(
    01)
                lcd.Write(wifi.ScannedSSID(i))
            
    End If
            Delay(
    100)
        
    Loop
        GlobalStore.Put(
    0, wifi.ScannedSSID(i)) 'SSID
    End Sub

    Sub EnterPassword    'Enter password for selected SSID via LCD UP/DOWN/LEFT/RIGHT/SELECT buttons
        Dim i As UInt = 0 'TextLine() line
        Dim j As UInt = 0 'Column within TextLine
        Dim char As String = ""
        
    Dim pwd As String = ""
        btn = 
    5 'Reset button value
        lcd.Clear
        lcd.Write(
    "Enter password")
        lcd.SetCursor(
    0,1)
        lcd.Write(TextLine(i))
        lcd.SetCursor(
    01)
        lcd.Blink = 
    True
        
    Do Until char = " "
            
    Dim reading As UInt = PIN_A0.AnalogRead
            
    For k = 0 To btnValues.Length - 1
                
    If reading < btnValues(k) Then
                    btn = k
                    
    Exit
                
    End If
            
    Next
            
    If btn = 1 And i > 0 Then 'UP
                i = i - 1
                lcd.Clear
                lcd.Write(pwd)
                lcd.SetCursor(
    01)
                lcd.Write(TextLine(i))
                lcd.SetCursor(
    01)
                lcd.Blink = 
    True
                j = 
    0
            
    End If
            
    If btn = 2 And i < 4 Then 'DOWN
                i = i + 1
                lcd.Clear
                lcd.Write(pwd)
                lcd.SetCursor(
    01)
                lcd.Write(TextLine(i))
                lcd.SetCursor(
    01)
                lcd.Blink = 
    True
                j = 
    0
            
    End If
            
    If btn = 3 And j > 0 Then 'LEFT
                j = j - 1
                lcd.SetCursor(j, 
    1)
            
    End If
            
    If btn = 0 And j < 15 Then 'RIGHT
                j = j + 1
                lcd.SetCursor(j, 
    1)
            
    End If
            
    If btn = 4 Then 'SELECT
                char = bc.StringFromBytes(bc.SubString2(TextLine(i), j, j+1))
                
    If char <> " " Then
                    pwd = JoinStrings (
    Array As String(pwd, char))
                    lcd.SetCursor(
    00)
                    lcd.Write(pwd)
                    lcd.SetCursor(j, 
    1)
                    
    Log(pwd)
                
    End If
            
    End If
            
    If btn = 3 And j = 0 And pwd <> "" Then 'BACKSPACE using LEFT button when cursor is at start of row
                pwd = bc.StringFromBytes(bc.SubString2(bc.StringToBytes(pwd),0, pwd.Length - 1))
                lcd.SetCursor(
    00)
                
    For c = 0 To pwd.Length + 1
                    lcd.write(
    " ")
                
    Next
                lcd.SetCursor(
    00)
                lcd.Write(pwd)
                lcd.SetCursor(j, 
    1)
                
    Log(pwd)
            
    End If
            Delay(
    100)
        
    Loop
        GlobalStore.Put(
    1, pwd) 'Password
        lcd.Clear
        lcd.Write(
    "Connecting to:")
        lcd.SetCursor(
    0,1)
        lcd.Write(GlobalStore.Slot0)
        WriteFile
    End Sub

    Sub WriteFile    'Writes password in file named with the SSID
        If fs.Exists(bc.StringFromBytes(GlobalStore.Slot0)) Then
            fs.Remove(bc.StringFromBytes(GlobalStore.Slot0))
        
    End If
        
    If fs.OpenReadWrite(bc.StringFromBytes(GlobalStore.Slot0)) Then
            
    Log("File successfully opened for write processing")
            
    Dim buffer(bc.StringFromBytes(GlobalStore.Slot1).length) As Byte = bc.StringFromBytes(GlobalStore.Slot1).GetBytes
            
    Log("Bytes written: ", fs.Stream.WriteBytes(buffer,0,buffer.Length))
            
    Log("File system total size ", fs.TotalSize)
            
    Log("File system used size ", fs.UsedSize)
            
    Log("File size ", fs.CurrentFile.size)
            fs.Close
        
    End If
    End Sub

    Sub ReadSSIDFile    'Reads password from file named with the SSID
        If fs.OpenRead(bc.StringFromBytes(GlobalStore.Slot0)) Then
            
    Log("File successfully opened for read processing")
            
    Log("File name ", fs.CurrentFile.Name)
            
    Log("File size ", fs.CurrentFile.size)
            fs.Position=
    0
            
    Dim buffer(fs.CurrentFile.size) As Byte
            
    Log("Number of bytes read: ", fs.Stream.ReadBytes(buffer,0,buffer.Length))
            
    Log("Position after reading file: ", fs.Position)
            
    Log("Data read: ", bc.StringFromBytes(buffer))
            GlobalStore.Put(
    1, bc.StringFromBytes(buffer))
            
    Log(GlobalStore.Slot0)
            
    Log(GlobalStore.Slot1)
            fs.Close
        
    Else
            
    Log("Cannot open file for read: ", bc.StringFromBytes(GlobalStore.Slot0))
        
    End If
    End Sub

    Sub CheckForPassword    'Checks to see if there is an existing password file for the selected SSID
        If fs.Exists(bc.StringFromBytes(GlobalStore.Slot0)) Then
            lcd.Clear
            lcd.Write(
    "Use existing?")
            lcd.SetCursor(
    01)
            lcd.Write(
    "Yes / No")
            btn = 
    5 'Reset button value
            Do While True 'Until LEFT or RIGHT pushed
                Dim reading As UInt = PIN_A0.AnalogRead
                
    For j = 0 To btnValues.Length - 1
                    
    If reading < btnValues(j) Then
                        btn = j
                        
    Exit
                    
    End If
                
    Next
                
    If btn = 3 Then 'Yes using LEFT button
                    ReadSSIDFile
                    lcd.Clear
                    lcd.Write(
    "Connecting to:")
                    lcd.SetCursor(
    0,1)
                    lcd.Write(GlobalStore.Slot0)
                    
    Return
                
    End If
                
    If btn = 0 Then 'No using RIGHT button
                    EnterPassword
                    
    Return
                
    End If
                Delay(
    100)
            
    Loop
        
    Else   
            EnterPassword
        
    End If
    End Sub

    Sub ListFiles
        
    Log("Listing files")
        
    For Each f As File In fs.ListFiles("")
            
    Log("Name: ", f.Name, ", Size: ", f.Size)
        
    Next
    End Sub

    Sub ConnectToNetwork
        
    If wifi.Connect2(bc.StringFromBytes(GlobalStore.slot0), bc.StringFromBytes(GlobalStore.Slot1)) = False Then
            
    Log("Error connecting to network")
            lcd.Clear
            lcd.write(
    "Error connecting")
            
    Return
        
    Else
            
    Log("Connected to network: ", GlobalStore.Slot0)
            lcd.Clear
            lcd.write(
    "Connected to:")
            lcd.SetCursor(
    0,1)
            lcd.Write(GlobalStore.Slot0)
            lcd.Blink = 
    False
            
    Log("My IP address: ", wifi.LocalIp)
            
    Dim index As UInt = 0
            
    For Each s() As Byte In bc.Split(wifi.localIP, ".")
                
    Dim b As Byte = bc.StringFromBytes(s)
                ip(index) = b
                index = index + 
    1
            
    Next
            ip(
    3) = 255
            
    Log("Broadcast IP address: ", ip(0), ".", ip(1), ".", ip(2), ".", ip(3))
            astream.Initialize(Serial1.Stream, 
    "astream_NewData""astream_Error")
            usocket.Initialize(
    51042"usocket_PacketArrived")
        
    End If   
    End Sub
     
    Johan Hormaza and Erel like this.
  8. Johan Hormaza

    Johan Hormaza Active Member Licensed User

    What kind of variable is GlobalStore? Thank you
     
  9. bdunkleysmith

    bdunkleysmith Member Licensed User

Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice