I'm tried using the Arduino IDE to program an RP2040 but failed. I am using currently thony with micropython. I purchased some from Amazon that looked sort of cute just to try out microPython. I want to use this post to share what I have so far projector. So far I figured out the connector that sends UART to the lamp. The UART is 2500, 9 bit and I've used a logic analyzer to decode it. It seems to come in a preamble set of back and forth communication followed by a constant 3 byte repeating pattern. I can now read the pattern and send the pattern but I have not been able to do it automatically. I was hoping someone more familiar could help me out with code ideas. Between some help from here and chatgpertee I might be able to bypass the lamp. Details to follow soon.
So I tried to put together a communication system but this doesn't seem to be working. When I remove the lamp the rp2040 seems to clamp the TX RX lines and the motherboard never sends UART requests:
from machine import Pin, UART, Timer
from rp2 import PIO, StateMachine, asm_pio
import utime
UART_BAUD = 2500
PIN_TX = 0
PIN_RX = 1
# Define the input byte sequences and their corresponding responses
input_sequences = {
(0x0009, 0x0000, 0x00F6): (0x0009, 0x0116, 0x01E0),
(0x0000, 0x0048, 0x00B7): (0x0000, 0x0000, 0x00FF),
(0x0101, 0x0000, 0x01FE): (0x0101, 0x0048, 0x01B6),
(0x0170, 0x0107, 0x0088): (0x0170, 0x0000, 0x018F),
(0x0011, 0x0101, 0x00ED): (0x0011, 0x0000, 0x00EE),
(0x01FF,): (0x0102, 0x0104, 0x00F9),
(0x0102, 0x0000, 0x01FD): (0x0102, 0x0104, 0x00F9),
(0x0104, 0x0000, 0x01FB): (0x0104, 0x0176, 0x0185),
(0x0003, 0x0000, 0x00FC): (0x0003, 0x001D, 0x01DF),
(0x0180, 0x0011, 0x016E): (0x0180, 0x0000, 0x017F),
(0x0011, 0x0000, 0x00EE): (0x0011, 0x0000, 0x00EE),
(0x0011, 0x0000): (0x0123, 0x0000, 0x01DC),
}
# Assembly code for UART transmission
@asm_pio(sideset_init=PIO.OUT_HIGH, out_init=PIO.OUT_HIGH, out_shiftdir=PIO.SHIFT_RIGHT)
def uart_tx():
# Block with TX deasserted until data available
pull()
# Initialise bit counter, assert start bit for 8 cycles
set(x, 8) .side(0) [7]
# Shift out 9 data bits, 9 execution cycles per bit
label("bitloop")
out(pins, 1) [7]
jmp(x_dec, "bitloop")
# Assert stop bit for 8 cycles total (incl 1 for pull())
nop() .side(1) [6]
# Initialize StateMachine for UART
sm = StateMachine(
0, uart_tx, freq=9 * UART_BAUD, sideset_base=Pin(PIN_RX), out_base=Pin(PIN_TX), in_base=Pin(PIN_RX)
)
sm.active(1)
# Function to bit-bang read a 9-bit data frame
def bit_bang_read_9bit(rx_pin):
# Wait for start bit
while rx_pin.value() == 1:
pass
# Wait half of bit time to sample in the middle of the bit
utime.sleep_us(int(500000 / UART_BAUD)) # Adjust based on baud rate
# Read 9 bits (assuming LSB-first)
value = 0
for i in range(9):
value |= (rx_pin.value() << i)
utime.sleep_us(int(1000000 / UART_BAUD)) # Adjust based on baud rate
return value
# Main loop for UART reception and response
while True:
received_value = bit_bang_read_9bit(Pin(PIN_RX))
print("Received:", hex(received_value))
utime.sleep_us(400) # Adjust delay as needed to match transmission rate
# Check if received value matches any input sequence
for seq, response in input_sequences.items():
if received_value == seq[0]: # Check only the first value of the sequence
sm.put(response[0]) # Send the first byte of the response
sm.put(response[1]) # Send the second byte of the response
sm.put(response[2]) # Send the third byte of the response
break # Exit the loop after matching and sending response
Here are some of my observations:
| query (motherboard RX) | response (lamp TX) | |||||
|---|---|---|---|---|---|---|
| 0x0123 | 0x0000 | 0x01DC | When powered on initially | |||
| 0x0009 | 0x0000 | 0x00F6 | 0x0009 | 0x0116 | 0x01E0 | query repeated 3 times if unanswered |
| 0x0000 | 0x0048 | 0x00B7 | 0x0000 | 0x0000 | 0x00FF | query repeated 3 times if unanswered |
| 0x0101 | 0x0000 | 0x01FE | 0x0101 | 0x0048 | 0x01B6 | query not used if first two go unanswered |
| 0x0170 | 0x0107 | 0x0088 | 0x0170 | 0x0000 | 0x018F | query repeated 3 times if unanswered |
| 0x0011 | 0x0101 | 0x00ED | 0x0011 | 0x0000 | 0x00EE | query repeats continuously |
| 0x01FF | 0x0102 | 0x0104 | 0x00F9 | start normal | ||
| 0x0102 | 0x0000 | 0x01FD | 0x0102 | 0x0104 | 0x00F9 | normal |
| 0x0104 | 0x0000 | 0x01FB | 0x0104 | 0x0176 | 0x0185 | |
| 0x0003 | 0x0000 | 0x00FC | 0x0003 | 0x001D | 0x01DF | |
| 0x0180 | 0x0011 | 0x016E | 0x0180 | 0x0000 | 0x017F | |
| 0x0011 | 0x0000 | 0x00EE | 0x0011 | 0x0000 | 0x00EE | Lamp Cool down |
| 0x0011 | 0x0000 | 0x0123 | 0x0000 | 0x01DC | Idle | |
| 0x01F0 | 0x0009 | 0x0000 | lamp has failed before | |||
| 0x00F6 | 0x0009 | 0x0000 | repeats twice | |||
| 0x00F6 | appears once | |||||
| 0x0000 | 0x0048 | 0x00B7 | repeats three times | |||
| 0x0170 | 0x0107 | 0x0088 | repeats three times | |||
| 0x0011 | 0x0101 | 0x00ED | Repeats 6 times |
So I changed strategies for a sec. I want to test a unilateral communication where I just send the time sequences. Sending for example just the continuous "0x0102 0x0104 0x00F9" seemed to keep the green status light on for a few more seconds than when there is no light-bulb connected.
from machine import Pin, UART, Timer
from rp2 import PIO, StateMachine, asm_pio
import utime
UART_BAUD = 2500
PIN_TX = 0
PIN_RX = 1
# Define the input byte sequences and their corresponding responses
input_sequences = {
(0x0009, 0x0000, 0x00F6): (0x0009, 0x0116, 0x01E0),
(0x0000, 0x0048, 0x00B7): (0x0000, 0x0000, 0x00FF),
(0x0101, 0x0000, 0x01FE): (0x0101, 0x0048, 0x01B6),
(0x0170, 0x0107, 0x0088): (0x0170, 0x0000, 0x018F),
(0x0011, 0x0101, 0x00ED): (0x0011, 0x0000, 0x00EE),
(0x01FF,): (0x0102, 0x0104, 0x00F9),
(0x0102, 0x0000, 0x01FD): (0x0102, 0x0104, 0x00F9),
(0x0104, 0x0000, 0x01FB): (0x0104, 0x0176, 0x0185),
(0x0003, 0x0000, 0x00FC): (0x0003, 0x001D, 0x01DF),
(0x0180, 0x0011, 0x016E): (0x0180, 0x0000, 0x017F),
(0x0011, 0x0000, 0x00EE): (0x0011, 0x0000, 0x00EE),
(0x0011, 0x0000): (0x0123, 0x0000, 0x01DC),
}
# Assembly code for UART transmission
@asm_pio(sideset_init=PIO.OUT_HIGH, out_init=PIO.OUT_HIGH, out_shiftdir=PIO.SHIFT_RIGHT)
def uart_tx():
# Block with TX deasserted until data available
pull()
# Initialise bit counter, assert start bit for 8 cycles
set(x, 8) .side(0) [7]
# Shift out 9 data bits, 9 execution cycles per bit
label("bitloop")
out(pins, 1) [7]
jmp(x_dec, "bitloop")
# Assert stop bit for 8 cycles total (incl 1 for pull())
nop() .side(1) [6]
# Initialize StateMachine for UART
sm = StateMachine(
0, uart_tx, freq=9 * UART_BAUD, sideset_base=Pin(PIN_RX), out_base=Pin(PIN_TX), in_base=Pin(PIN_RX)
)
sm.active(1)
def send_sequence(seq, repeat=1, delay_ms=90):
for _ in range(repeat):
for byte in seq:
sm.put(byte)
utime.sleep_us(100) # Adjust this delay if necessary
utime.sleep_ms(delay_ms) # Delay between repetitions
# Main loop for UART reception and response
utime.sleep_ms(2200) # wait before first sequence
send_sequence(input_sequences[0x0009, 0x0000, 0x00F6], repeat=1, delay_ms=1)
utime.sleep_ms(122) # wait before first sequence
send_sequence(input_sequences[0x0000, 0x0048, 0x00B7], repeat=1, delay_ms=1)
utime.sleep_ms(120) # wait before first sequence
send_sequence(input_sequences[0x0101, 0x0000, 0x01FE], repeat=1, delay_ms=1)
utime.sleep_ms(131) # wait before first sequence
send_sequence(input_sequences[0x0170, 0x0107, 0x0088], repeat=1, delay_ms=1)
utime.sleep_ms(341) # wait before first sequence
send_sequence(input_sequences[0x0011, 0x0101, 0x00ED], repeat=1, delay_ms=1)
utime.sleep_ms(2100) # wait before first sequence
send_sequence(input_sequences[0x0102, 0x0000, 0x01FD], repeat=80, delay_ms=90) # Send the sequence 80 times with 90ms delay
utime.sleep_ms(121) # wait before first sequence
send_sequence(input_sequences[0x0011, 0x0000], repeat=1, delay_ms=1) # there is actually no request here, it just appears
utime.sleep_ms(44) # wait before first sequence
send_sequence(input_sequences[0x0102, 0x0000, 0x01FD], repeat=8, delay_ms=378) # Send the sequence 80 times with 90ms delay
utime.sleep_ms(2114) # wait before first sequence
send_sequence(input_sequences[0x0180, 0x0011, 0x016E], repeat=1, delay_ms=1)
utime.sleep_ms(218) # wait before first sequence
send_sequence(input_sequences[0x0102, 0x0000, 0x01FD], repeat=1, delay_ms=1)
utime.sleep_ms(706) # wait before first sequence
#lightbulb started, now we just repeat the same sequence
while True:
# Iterate through the input sequences
#print("Sequence:", ", ".join(f"0x{byte:04X}" for byte in input_sequences[0x0009, 0x0000, 0x00F6]))
send_sequence(input_sequences[0x0102, 0x0000, 0x01FD], repeat=1, delay_ms=1)
utime.sleep_ms(373)
Finally, here is an image from the logic analyzer just to give you an idea of the communication. The bottom channel seems to be a clock signal. the top seems to be High only when the lamp is on.... I haven't tried just simply making that pin go high on the rp2040 to see if that makes the thing magically work... it couldn't be that simple right??
The brown signal in CH1 in this case is the request from the motherboard... I think that's the case because it repeats the same request if I disconnect the lamp. The CH2 signal is the lamp. you can see that the lamp responds with 3 bytes next to each other while the motherboard sends 3 bytes with a larger spacing. I don't know why that might be. Maybe I can use some of this signal to trigger the rpi2040 to start sending its signal? I am not 100% sure where to start or well continue with this. I am sort of chatgpt stumbling at this point, thus my post on the forum.
I am puzzled by the high signal on ch0 and the clock on ch3. the clock stabilizes but starts with a weird almost PWM ramping like this:
But the most puzzling part is that the motherboard and lamp query responses often just overrun each other like they were not really correlating... more like just query query..answer, query, answer answer.. not really at random since the two signals are relatively equally spaced from message to message, but maybe not really a query/answer but more like Hey! Hey too! the difference between ping-pong and just ping-ping (do not ask any filipina what a ping ping signal is). Anyway, for that reason I think that just playing the whole communication is probably going to get the job done? but still running into problems. Right now I am trying to get the signal out but the print statements are not resulting the in the correct hex codes. That is why I changed over to bit banging the code out. I will try this soon tonight maybe and then report back. If you got clever ideas to try, I'm all ears.



