B4R Question Button selection via A0 on ESP8266

bdunkleysmith

Active Member
Licensed User
Longtime 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.

B4X:
Sub Process_Globals
    Public Serial1 As Serial
    Private usocket As WiFiUDP
    Private wifi As ESP8266WiFi
    Private ip(4) As Byte
    Private port As UInt = 3661
    Private astream As AsyncStreams
    Private bc As ByteConverter
    Private lcd As LiquidCrystal
    Private TextLine(5) As 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(50, 250, 450, 650, 850, 1025) ' 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(0, 1)
        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?
 

bdunkleysmith

Active Member
Licensed User
Longtime 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:

B4X:
Private Sub AppStart
    Serial1.Initialize(115200)
    lcd.Initialize(16, 255, 5, Array As Byte (12, 13, 14, 15))
    lcd.Begin(16, 2)
    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(0, 1)
            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:

B4X:
rivate Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    lcd.Initialize(16, 255, 5, Array As Byte (12, 13, 14, 15))
    lcd.Begin(16, 2)
    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(0, 1)
    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(0, 1)
            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(0, 1)
            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:

B4X:
rivate Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    lcd.Initialize(16, 255, 5, Array As Byte (12, 13, 14, 15))
    lcd.Begin(16, 2)
    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(0, 1)
    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(0, 1)
            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(0, 1)
            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:
Upvote 0

bdunkleysmith

Active Member
Licensed User
Longtime 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.
 
Upvote 0

bdunkleysmith

Active Member
Licensed User
Longtime 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.

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

Private Sub AppStart
    Serial1.Initialize(115200)
    lcd.Initialize(16, 255, 5, Array As Byte (12, 13, 14, 15))
    lcd.Begin(16, 2)
    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),0, 1)) = "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),0, 1)) = "S" Then
                lcd.SetCursor(6,0)
                lcd.Write(bc.StringFromBytes(bc.SubString2((s),1,11)))
            End If
            If bc.StringFromBytes(bc.SubString2((s),0, 1)) = "H" Then
                lcd.setcursor(0, 1)
                lcd.Write(bc.StringFromBytes(bc.SubString2((s),1,3)))
            End If
            If bc.StringFromBytes(bc.SubString2((s),0, 1)) = "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(0, 1)
    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(0, 1)
            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(0, 1)
            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(0, 1)
    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(0, 1)
            lcd.Write(TextLine(i))
            lcd.SetCursor(0, 1)
            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(0, 1)
            lcd.Write(TextLine(i))
            lcd.SetCursor(0, 1)
            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(0, 0)
                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(0, 0)
            For c = 0 To pwd.Length + 1
                lcd.write(" ")
            Next
            lcd.SetCursor(0, 0)
            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(0, 1)
        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
 
Upvote 0
Top