MrYsLab / s3-extend

Control an Arduino Uno, Circuit Playground Express, Raspberry Pi Pico, ESP-8266, ESP-32, Picoboard, Robohat MM1, or Rasberry Pi From Scratch 3
GNU Affero General Public License v3.0
27 stars 13 forks source link

No errors but "blink sketch" doesn't work on esp8266 #33

Closed Anton-V-K closed 2 years ago

Anton-V-K commented 2 years ago

I've followed your great tutorial trying to imitate standard Blink sketch in Scratch, but it just doesn't work - the LED doesn't blink. All servers are running fine, even the firmware indicates that the client connects when I start the scratch program. OS: Windows 10 Home 64-bit (Version 10.0.18362) MCU: LoLin esp8266 (NodeMCU V3) as "NodeMCU 1.0 (ESP12-E Module)" in Arduino's Boards Manager. Arduino 1.8.15

The sketch I've used to test the LED:

#define LED_PIN D1 // D1=GPIO5 (for external LED) or LED_BUILTIN (for built-in LED)

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_PIN, HIGH);        // turn the LED on (HIGH is the voltage level)
  delay(1000);                        // wait for a second
  digitalWrite(LED_PIN, LOW);         // turn the LED off by making the voltage LOW
  delay(1000);                        // wait for a second
}

The Scratch blocks: ESP8266_LED I assume the pin numbers are referencing to GPIO numbers. Is this so? Here is the project for your reference - ESP8266_LED.sb3.zip

s3e normally doesn't work (even after reboot):

C:\Users\Anton>s3e
Traceback (most recent call last):
  File "C:\Program Files (x86)\Python310-32\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Program Files (x86)\Python310-32\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:\Program Files (x86)\Python310-32\Scripts\s3e.exe\__main__.py", line 7, in <module>
  File "C:\Program Files (x86)\Python310-32\lib\site-packages\s3_extend\s3e.py", line 202, in s3ex
    S3E()
  File "C:\Program Files (x86)\Python310-32\lib\site-packages\s3_extend\s3e.py", line 49, in __init__
    self.proc_bp = self.start_backplane()
  File "C:\Program Files (x86)\Python310-32\lib\site-packages\s3_extend\s3e.py", line 151, in start_backplane
    if 'backplane' in proc.info['name']:
TypeError: argument of type 'NoneType' is not iterable

So I proceed with running servers individually:

C:\Users\Anton>backplane

******************************************
Backplane IP address: 192.168.1.48
Subscriber Port = 43125
Publisher  Port = 43124
Loop Time = 0.001 seconds
******************************************
C:\Users\Anton>espgw
TelemetrixAIO Version: 1.7
Copyright (c) 2018-2020 Alan Yorinks All rights reserved.

************************************************************
ESP8266Gateway using Back Plane IP address: 192.168.1.48
Subscriber Port = 43125
Publisher  Port = 43124
************************************************************
set_pin_mode: callback ignored for pin state: 1
Successfully connected to: 192.168.1.64:31335
Telemetrix4Arduino Version Number: 3.0.0

The last 3 (three) lines above are shown when I start the scratch program, so it apparently does connect to ESP8266.

C:\Users\Anton>wsgw -i 9002

************************************************************
WebSocket Gateway using Back Plane IP address: 192.168.1.48
Subscriber Port = 43125
Publisher  Port = 43124
************************************************************
WebSocket using: 192.168.1.48:9002

The firmware also runs fine:

Connected to WiFi. IP Address: 192.168.1.64  IP Port: 31335
Client Connected to address: 192.168.1.48

PC restart doesn't help. I've also tried running Scratch online and locally.

Where else to look? What could be the problem?

MrYsLab commented 2 years ago

I think the LED pin numbers for the nodeMCU are 2 and 16 according to the EPS8266 Arduino core. Could you please try 2 and/or 16 and let me know if that works? Thanks

Anton-V-K commented 2 years ago

@MrYsLab , thanks for the reply. I use an external LED which is connected to D1, which is GPIO5. It works as expected when ESP8266 is flashed with the sketch I listed above. BTW 2 isn't possible to select in Write Digital Pin block - possible values are 4, 5, 12, 13, 14, 15.

MrYsLab commented 2 years ago

Ok. I will need to investigate this to see if I broke anything. Please give me a couple of days to take a look at the issue. Thanks.

MrYsLab commented 2 years ago

@Anton-V-K I just tested using GPIO pin 5, and it is working here. Here are some things for you to check:

  1. When you first power up the ESP-8266, if you have the Arduino serial monitor connected, is the assigned IP address printed to the screen?

You should see something like this:

Allow 15 seconds for connection to complete.........
Connected to WiFi. IP Address: 192.168.2.112  IP Port: 31335
Client Connected to address: 192.168.2.104

Here, the IP address of my ESP-8266 is 192.168.2.112, and it is connecting to my Linux PC, which has an IP address of 192.168.2.104

I connect Scratch using the following block: block

  1. Make sure your LED is connected to GPIO 5 and not D5 as labeled on the NodeMCU. The pin numbers I use are GPIO pin numbers. nodemcu

  2. If the previous things are all correct and it is still not working, after starting s3e, open up a second command Window on your PC and type:

monitor

Then run your Scratch script. In the monitor window, you should see something like:

m

The 10.0.2.15 address is the address of my Windows box, which I run in VirtualBox in Linux. This is not important, I just mention it in case the differences in IP addresses is confusing.

This screen shows the communication between Scratch and s3e.

Hopefully, this helps to solve your issue. If you are able to resolve the issue, please close this issue, but if not, let me know if any of the suggestions above give you different results. Thanks,

MrYsLab commented 2 years ago

I forgot to mention that you also need to edit the Arduino sketch for your network SSID and Password: At line 30 of the sketch:

// Modify the next two lines to match your network values
const char *ssid = "YOUR_SSID";
const char *password = "YOUR_PASSWORD";
Anton-V-K commented 2 years ago

@MrYsLab , thanks for the guidelines. Here is what monitor shows:

C:\Users\Anton>monitor

************************************************************
Monitor using Back Plane IP address: 192.168.1.48
Subscriber Port = 43125
Publisher  Port = 43124
Loop Time = 0.1 seconds
************************************************************
to_esp8266_gateway {'command': 'ip_address', 'address': '192.168.1.64'}
to_esp8266_gateway {'command': 'set_mode_digital_output', 'pin': 5}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 1}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 0}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 1}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 0}

So I can conclude the scratch program communicates with ESP8266 correctly.

I guess, the issue may be related to wiring. Here is how I connect everyting (the LED module contains the limiting resistors): ESP8266_LED-wiring

I think, the scratch program doesn't perform proper pin tuning - setting pin mode to OUTPUT is usually needed. To test this I removed pinMode(LED_PIN, OUTPUT) from the sketch above, and the LED stopped blinking. Probably the wiring should be done in a special way. Thus straightforward porting of "blink sketch" to Scratch isn't possible.

Any ideas?

MrYsLab commented 2 years ago

The pin is set to output as shown in the monitor line:

to_esp8266_gateway {'command': 'set_mode_digital_output', 'pin': 5}

I connected a discrete LED using the following instructions from this article:

Connect one end of the resistor to the digital pin correspondent to the LED_BUILTIN constant.

Connect the long leg of the LED (the positive leg, called the anode) to the other end of the resistor.

Connect the short leg of the LED (the negative leg, called the cathode) to the GND. 

The Scratch script I am using is shown below. I manually click on the IP address block once and then use the green flag to run the script.

It blinks the light as expected.

I don't know how your RGB board is wired, but using a single discrete LED works. I am not doing anything special in terms of wiring. If you can, could you please try using a discrete LED and resistor to test? Screenshot from 2021-10-15 17-21-00

Anton-V-K commented 2 years ago

I've changed the wiring to use pure LED and a resistor... and it has no posistive effect: Arduino-style sketch works as expected, and Scratch-style program doesn't. I can conclude, the problem isn't caused by wiring.

I've modified the sketch Telemetrix4Esp8266.ino (taken from the official examples) to add serial logging into get_next_command:

...
  // uncomment the next line to see the packet length and command
  //send_debug_info(packet_length, command);
  command_entry = command_table[command];
  Serial.printf("%s: packet_length = %d, command = %d\n", __func__, int(packet_length), int(command)); // <<< added <<<
...

Here is the serial monitor log:

Allow 15 seconds for connection to complete......
Connected to WiFi. IP Address: 192.168.1.64  IP Port: 31335
Client Connected to address: 192.168.1.48
get_next_command: packet_length = 1, command = 5
get_next_command: packet_length = 1, command = 17
get_next_command: packet_length = 1, command = 18
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2

The relevant commands are:

#define SET_PIN_MODE 1
#define DIGITAL_WRITE 2
#define GET_FIRMWARE_VERSION 5
#define ENABLE_ALL_REPORTS 17
#define RESET_DATA_STRUCTURES 18

Unfortunately the command SET_PIN_MODE (1) doesn't reach esp8266, though it is logged by monitor (refer to the log from my previous comment).

MrYsLab commented 2 years ago

I cannot reproduce your problem, so I am confused as to what might be happening. Do you have version 3.0.2 of the ESP8266 Arduino board library installed in the Arduino IDE?

If you do, one thing you might try is creating a scratch script that looks like this:

Screenshot from 2021-10-16 11-12-31

This script will send a "set mode to input," which may get lost, but the "set mode output" will be sent after that, and hopefully, it will not get lost.

If this script succeeds, you can try removing the wait blocks before the forever blocks, one by one, to see if that affects the outcome.

If none of this works, I do not know what to try next. The following statement doesn't help your issue, but hundreds of students in Taiwan use the ESP8266 Scratch extension without any problems.

Anton-V-K commented 2 years ago

@MrYsLab , your hack does work! I was able to blink LED after that :) Moreover reading from the pin "digitally" before the loop also does the trick, and no delay is needed: ESP8266_LED-debug Here is the monitor log:

C:\Users\Anton>monitor

************************************************************
Monitor using Back Plane IP address: 192.168.1.48
Subscriber Port = 43125
Publisher  Port = 43124
Loop Time = 0.1 seconds
************************************************************
to_esp8266_gateway {'command': 'ip_address', 'address': '192.168.1.64'}
to_esp8266_gateway {'command': 'set_mode_digital_input', 'pin': 5}
to_esp8266_gateway {'command': 'set_mode_digital_output', 'pin': 5}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 1}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 0}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 1}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 0}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 1}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 0}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 1}

Here is the log from esp8266:

Connected to WiFi. IP Address: 192.168.1.64  IP Port: 31335
Client Connected to address: 192.168.1.48
get_next_command: packet_length = 1, command = 5
get_next_command: packet_length = 1, command = 17
get_next_command: packet_length = 1, command = 18
get_next_command: packet_length = 3, command = 1
set_pin_mode: pin = 5, mode = 1
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2

Note that esp8266 got only one SET_PIN_MODE (1) command for pin GPIO5 to configure it as OUTPUT (1).

During further experiments I noticed that pin mode command could be skipped completely (it isn't shown in monitor at all!):

C:\Users\Anton>monitor

************************************************************
Monitor using Back Plane IP address: 192.168.1.48
Subscriber Port = 43125
Publisher  Port = 43124
Loop Time = 0.1 seconds
************************************************************
to_esp8266_gateway {'command': 'ip_address', 'address': '192.168.1.64'}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 1}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 0}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 1}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 0}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 1}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 0}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 1}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 0}
to_esp8266_gateway {'command': 'digital_write', 'pin': 5, 'value': 1}

Probably PC reboot is needed... It seems there is an issue in your scripts which makes their behavior unpredictable in some environments or/and under some conditions.

MrYsLab commented 2 years ago

Would you be willing to try one other test? I have test programs for both asyncio (which is used by s3-extend) and non-asyncio that blink an LED.

For both, you would need to edit the IP address to match that of your ESP8266 and change the pin number for the LED.

I would be curious to know if either or both of these programs successfully blink the LED.

Anton-V-K commented 2 years ago

@MrYsLab , both tests run fine in my environment - the LED is blinking in both cases. I had to install telemetrix in order to execute these scripts. Here is the log of asyncio-based version:

...\asyncio-blink_wifi.py:53: DeprecationWarning: There is no current event loop
  loop = asyncio.get_event_loop()
TelemetrixAIO Version: 1.7
Copyright (c) 2018-2020 Alan Yorinks All rights reserved.

Successfully connected to: 192.168.1.64:31335
Telemetrix4Arduino Version Number: 3.0.0
set_pin_mode: callback ignored for pin state: 1
ON
OFF
ON
OFF
ON
OFF
ON
OFF

Here is the corresponding esp8266 log:

Client Connected to address: 192.168.1.48
get_next_command: packet_length = 1, command = 5
get_next_command: packet_length = 1, command = 17
get_next_command: packet_length = 1, command = 18
get_next_command: packet_length = 3, command = 1
set_pin_mode: pin = 5, mode = 1
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 1, command = 15
Client disconnected
MrYsLab commented 2 years ago

That was very helpful thanks. I found a typo in esp8266_gateway.py on line 81. The line was improperly indented.

I have created version 1.19 of s3-extend. Could you please update to this version:

pip install s3-extend --upgrade

Please let me know if this solves the problem. Thanks.

Anton-V-K commented 2 years ago

@MrYsLab , thanks for the fix. I'm not sure whether it solves the problem completely. Actually it still happens, and I can provide more info based on the investigations at my side. I was about to blame my browser (Brave 1.30.87), because starting the scratch project in any another browser made the LED start blinking. However it turned out that the scratch project doesn't work in any browser where it is opened first time (after PC reboot). And the test scenario is as follows:

  1. Open the scratch project in Browser 1 and start it => LED doesn't blink (set_pin_mode isn't called).
  2. Stop the scratch project.
  3. Start the opened scratch project again in the same browser => LED doesn't blink (set_mode_digital_output isn't sent in this case at all... why?)
  4. Stop the scratch project.
  5. Open the scratch project in Browser 2 and start it => everything works as expected.
  6. Stop the scracht project.
  7. Start the opened scratch project in Browser 1 => everything works as expected (apparently the pin mode had already been configured at the step 5).

Tested browsers:

MrYsLab commented 2 years ago

Thanks for providing that information. The problem is consistent in that the first message coming from your browsers somehow does not get executed. In Scratch, the "Write Digital Block" sets the pin mode for a pin the first time the block is executed. After that, only the command to set the pin's state is sent. The Monitor's output reflects this behavior.

I see you are using 32-bit browsers. I assume then that your version of Windows 10 is 32 bit as well. If that is true, I suspect that somehow, the 32-bit version of either the browsers, Windows 10, or a combination of the two is causing this behavior.

I do not have access to a 32-bit system, so I cannot test this theory or determine its cause.

If you have access to a 64-bit system and run the program, my guess is that the program will run as expected. As I mentioned earlier, several hundred students use the ESP-8266 with Scratch, but I know that they are using 64-bit software versions.

At this point, I am not sure how to proceed. If using a "dummy" read block as the first instruction after executing the IP address block is not objectionable, this may be the simplest solution.

Anton-V-K commented 2 years ago

Actually my Windows 10 is 64-bit. But the majority of installed software (including all browsers) is 32-bit. I also have Windows 7 on another laptop. I'll check whether the issue happens there (as soon as I'll manage to run gateways - see #34). I doubt the issue is caused by "bitness mismatch". At first sight it looks like a kind of "uninitialized variable" somewhere in the scripts.

MrYsLab commented 2 years ago

After looking over the code, I do not see anything that is incorrect. Of course, there may be a bug that I don't see, but since I cannot reproduce the issue, I don't know how to resolve the issue. I checked the version of Windows 10 I am running, and it is Version 10.0.19043 Build 19043. The version you are running is anything from a year to a year and a half old. The Windows 10 version may or may not be the issue, but Python 3 asyncio had some problems running an earlier version of Windows.

MrYsLab commented 2 years ago

@Anton-V-K I just tried running on a 32-bit Linux laptop that is about 20 years old. I can start s3e without any problems, but Chrome and Firefox will not load the Scratch editor. Neither support WebGL, needed by Scratch. I don't know if this is peculiar to the 32 bit Linux browsers.

I was hoping to be able to try and reproduce the problem using older hardware and 32-bit software.

I have one more thing to try. I will write a Python WebSocket script to replace Scratch. This script will generate the same messages as Scratch, but all the other code in the messaging chain will be the same - the WebSocket gateway, the esp8266 gateway, and the ESP8266 sketch. If this sketch can successfully control the LED, then I am out of ideas.

It may take me a day or two to write and test the WebSocket script, but I will let you know the outcome. If you have any ideas on how to reproduce what you are seeing, please let me know.

MrYsLab commented 2 years ago

I created the script, and I can reproduce what you are seeing. I need to do some more investigation to determine where the messages are getting lost and why. The script sends an ID message, an IP address message, and the set mode message with no delays between them. The loop toggling the pin has a 0.02 sleep in between each toggle. If I add a 0.2 sleep after the ID, IP address, and set mode messages, all works as expected.

If you wish to run the script, it is located at this link.

You will need to change the IP address to match your ESP-8266, and if you are using a pin number other than 5, change the pin number to match your setup. These modifications are all commented on in the script.

To run the script, start s3e, then begin the test script using Python 3. If the script fails, please increase the sleep times and let me know the values you need to set. If the script runs, please comment out the sleep statements before the while loop and let me know if it fails.

Thanks

Anton-V-K commented 2 years ago

@MrYsLab , thanks for looking into this. The emulator script works fine without changing the delays - the LED is blinking very fast. If I remove all delays at the setup phase (before the loop), the issue comes back - set_pin_mode isn't called, and the LED isn't blinking.

MrYsLab commented 2 years ago

Ok. So now that it is reproducible, all I need to do is find the cause ;-)

MrYsLab commented 2 years ago

A quick update. I found the cause of the problem when running the WebSocket test script, and I think it is probably the cause of what you see in your environment. The ESP8266 gateway is a special case compared to all the other gateways. The gateway has a continuous running task that receives incoming messages from Scratch. When the gateway receives the message from the Scratch IP address block, it initiates the IP connection to the ESP8266. While this connection is in progress, the set_pin_mode_digtial_output is received, and it tries to send out the command to the ESP8266 even though the connection is not complete.

I need to decide how to fix this race condition. There are a couple of options. The first is using an asyncio synchronization primitive between the two tasks.

The other is to run the message receiving task to receive a single IP address message and then exit. This message initiates the IP connection, and when the connection completes, the message receiving task is started again to run continuously. I believe the system will buffer any messages sent from Scratch when the receive task is down waiting for the IP connection. I need to verify that this is true, and if it is, I will probably be going with this second.

I will be taking the next few days to implement and test things out. When it is complete, I will let you know.

MrYsLab commented 2 years ago

I have just uploaded the fixes to pypi.

pip install s3-extend --upgrade

This update should not only update s3-extend but telemetrix-aio and python_banyan as well.

Please let me know if this resolves the issue for you.

Thanks for your patience.

Anton-V-K commented 2 years ago

@MrYsLab , thank you very much for your efforts! After the update of s3-extend the scratch program started to work as expected - the LED is blinking! :) Here the log from the serial monitor:

Connected to WiFi. IP Address: 192.168.1.64  IP Port: 31335
Client Connected to address: 192.168.1.48
get_next_command: packet_length = 1, command = 5
get_next_command: packet_length = 1, command = 17
get_next_command: packet_length = 1, command = 18
get_next_command: packet_length = 4, command = 1
set_pin_mode: pin = 5, mode = 1
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
get_next_command: packet_length = 3, command = 2
MrYsLab commented 2 years ago

Thanks for letting me know and filing the issue. I still don't understand why the race condition did not exhibit itself on any of my systems, but I won't worry about it. I am just glad that it is fixed.

Again thanks for your patience.