From bea41806cd789483353dec65f263995d11c1361f Mon Sep 17 00:00:00 2001
From: jwagner <jan.wagner@iki.fi>
Date: Tue, 19 Dec 2023 20:15:08 +0100
Subject: [PATCH 1/2] add Wire::onRequestMore()

---
 libraries/Wire/src/Wire.cpp      | 19 +++++++++++++++++++
 libraries/Wire/src/Wire.h        |  3 +++
 libraries/Wire/src/utility/twi.c | 16 ++++++++++++++++
 libraries/Wire/src/utility/twi.h |  1 +
 4 files changed, 39 insertions(+)

diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp
index 001d924df..bdff763dc 100644
--- a/libraries/Wire/src/Wire.cpp
+++ b/libraries/Wire/src/Wire.cpp
@@ -43,6 +43,7 @@ uint8_t TwoWire::txBufferLength = 0;
 
 uint8_t TwoWire::transmitting = 0;
 void (*TwoWire::user_onRequest)(void);
+void (*TwoWire::user_onRequestMore)(void);
 void (*TwoWire::user_onReceive)(int);
 
 // Constructors ////////////////////////////////////////////////////////////////
@@ -63,6 +64,7 @@ void TwoWire::begin(void)
 
   twi_init();
   twi_attachSlaveTxEvent(onRequestService); // default callback must exist
+  twi_attachSlaveTxMoreEvent(onRequestMoreService); // default callback must exist
   twi_attachSlaveRxEvent(onReceiveService); // default callback must exist
 }
 
@@ -360,6 +362,17 @@ void TwoWire::onRequestService(void)
   user_onRequest();
 }
 
+// behind the scenes function that is called when more data is requested
+void TwoWire::onRequestMoreService(void)
+{
+  // don't bother if user hasn't registered a callback
+  if(!user_onRequestMore){
+    return;
+  }
+  // alert user program
+  user_onRequestMore();
+}
+
 // sets function called on slave write
 void TwoWire::onReceive( void (*function)(int) )
 {
@@ -372,6 +385,12 @@ void TwoWire::onRequest( void (*function)(void) )
   user_onRequest = function;
 }
 
+// sets function called on slave read
+void TwoWire::onRequestMore( void (*function)(void) )
+{
+  user_onRequestMore = function;
+}
+
 // Preinstantiate Objects //////////////////////////////////////////////////////
 
 TwoWire Wire = TwoWire();
diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h
index e70d72edb..e3620e241 100644
--- a/libraries/Wire/src/Wire.h
+++ b/libraries/Wire/src/Wire.h
@@ -45,8 +45,10 @@ class TwoWire : public Stream
 
     static uint8_t transmitting;
     static void (*user_onRequest)(void);
+    static void (*user_onRequestMore)(void);
     static void (*user_onReceive)(int);
     static void onRequestService(void);
+    static void onRequestMoreService(void);
     static void onReceiveService(uint8_t*, int);
   public:
     TwoWire();
@@ -75,6 +77,7 @@ class TwoWire : public Stream
     virtual void flush(void);
     void onReceive( void (*)(int) );
     void onRequest( void (*)(void) );
+    void onRequestMore( void (*)(void) );
 
     inline size_t write(unsigned long n) { return write((uint8_t)n); }
     inline size_t write(long n) { return write((uint8_t)n); }
diff --git a/libraries/Wire/src/utility/twi.c b/libraries/Wire/src/utility/twi.c
index e09a33caf..391e09b1a 100644
--- a/libraries/Wire/src/utility/twi.c
+++ b/libraries/Wire/src/utility/twi.c
@@ -56,6 +56,7 @@ static volatile bool twi_timed_out_flag = false;  // a timeout has been seen
 static volatile bool twi_do_reset_on_timeout = false;  // reset the TWI registers on timeout
 
 static void (*twi_onSlaveTransmit)(void);
+static void (*twi_onSlaveTransmitMore)(void);
 static void (*twi_onSlaveReceive)(uint8_t*, int);
 
 static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
@@ -384,6 +385,17 @@ void twi_attachSlaveTxEvent( void (*function)(void) )
   twi_onSlaveTransmit = function;
 }
 
+/* 
+ * Function twi_attachSlaveTxMoreEvent
+ * Desc     sets function called before a slave cont'd sequential data write operation
+ * Input    function: callback function to use
+ * Output   none
+ */
+void twi_attachSlaveTxMoreEvent( void (*function)(void) )
+{
+  twi_onSlaveTransmitMore = function;
+}
+
 /* 
  * Function twi_reply
  * Desc     sends byte or readys receive line
@@ -640,6 +652,10 @@ ISR(TWI_vect)
     case TW_ST_DATA_ACK: // byte sent, ack returned
       // copy data to output register
       TWDR = twi_txBuffer[twi_txBufferIndex++];
+	  // if the buffer emptied, request it to be topped up
+	  if (twi_txBufferIndex >= twi_txBufferLength) {
+		twi_onSlaveTransmitMore();
+	  }
       // if there is more to send, ack, otherwise nack
       if(twi_txBufferIndex < twi_txBufferLength){
         twi_reply(1);
diff --git a/libraries/Wire/src/utility/twi.h b/libraries/Wire/src/utility/twi.h
index 85b983794..ea158d8ea 100644
--- a/libraries/Wire/src/utility/twi.h
+++ b/libraries/Wire/src/utility/twi.h
@@ -49,6 +49,7 @@
   uint8_t twi_transmit(const uint8_t*, uint8_t);
   void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) );
   void twi_attachSlaveTxEvent( void (*)(void) );
+  void twi_attachSlaveTxMoreEvent( void (*)(void) );
   void twi_reply(uint8_t);
   void twi_stop(void);
   void twi_releaseBus(void);

From 3dec52ea7fb1e00b84ee361f13bb86e50102bcb6 Mon Sep 17 00:00:00 2001
From: jwagner <jan.wagner@iki.fi>
Date: Tue, 19 Dec 2023 22:45:45 +0100
Subject: [PATCH 2/2] add example for onRequestMore event

---
 .../examples/slave_memory/slave_memory.ino    | 104 ++++++++++++++++++
 1 file changed, 104 insertions(+)
 create mode 100644 libraries/Wire/examples/slave_memory/slave_memory.ino

diff --git a/libraries/Wire/examples/slave_memory/slave_memory.ino b/libraries/Wire/examples/slave_memory/slave_memory.ino
new file mode 100644
index 000000000..942aa023f
--- /dev/null
+++ b/libraries/Wire/examples/slave_memory/slave_memory.ino
@@ -0,0 +1,104 @@
+// Wire Slave "256-Byte Memory" Device
+// by Jan Wagner
+
+// Demonstrates use of the Wire library to emulate a 256-byte memory device,
+// with sequential continued read and sequential continued write access.
+// Operates as an I2C/TWI slave device.
+//
+// The protocol used in this example emulates typical small EEPROMs,
+// or i2c-addressible register banks. The master controls the I2C transaction,
+// doing one of two things within a single transaction:
+//
+// 1) master sends an address-byte and sends optional data-byte(s) that the
+//    slave shall store into memory starting from the given address,
+//
+//  or
+//
+// 2) master sends an address-byte and immediately changes into receive
+//    mode, receiving data-byte(s) from slave memory starting from that address.
+//  
+// The number of data bytes in the transaction is not known in advance.
+// The master can at any time stop sending (1) or reading (2) data bytes.
+// Either mode, (1) or (2), is carried out as a single multi-byte I2C transaction.
+//
+// Starting from a base address (between 0 and 255) sent by the master,
+// the slave auto-increments the address for each byte sent or read.
+// When the end of address space is reached it wraps back to 0.
+
+// Master writing address, then writing data (case 1) is handled simply in the
+//   onReceive event - receives starting address plus data bytes from master
+
+// Master writing address, then reading data (2) is handled in the
+//   onReceive event - receives starting address from master, no other data
+//   onRequest event - sends the first requested data to master
+//   onRequestMore event - sends more, continually reinvoked while master keeps reading
+
+// Created 18 December 2023
+
+// This example code is in the public domain.
+
+#include <Wire.h>
+
+static volatile byte shared_memory[256];
+static volatile unsigned int memory_addr = 0;
+
+void setup() {
+  unsigned int i;
+
+  // initialize memory with a simple pattern
+  for(i=0; i<sizeof(shared_memory); i++) {
+    shared_memory[i] = i;
+  }
+  memory_addr = 0;
+  
+  // initialise Wire
+  Wire.begin(8);                // join I2C bus with address #8
+  Wire.onReceive(receiveEvent); // register event
+  Wire.onRequest(requestEvent); // register event
+  Wire.onRequestMore(requestMoreEvent); // register event
+}
+
+void loop() {
+  delay(100);
+}
+
+// Helper to get next address within array bounds
+unsigned int next_addr(unsigned int curr_addr) {
+  return (curr_addr + 1) % (sizeof(shared_memory) + 1);
+}
+
+// Function that executes whenever data is sent to slave by master
+// This function is registered as an event, see setup()
+void receiveEvent(int nbytes) {
+  if(nbytes > 0) {
+
+    // receive the memory address that the master wants to access
+    memory_addr = Wire.read();
+    memory_addr = memory_addr % (sizeof(shared_memory) + 1);
+    nbytes--;
+
+    // receive optional data that master might be pushing into client memory
+    while(nbytes > 0) {
+      shared_memory[memory_addr] = Wire.read();
+      memory_addr = next_addr(memory_addr);
+      nbytes--;
+    }
+  }
+}
+
+// Function that executes whenever data is first requested by master
+// This function is registered as an event, see setup()
+void requestEvent() {
+  // master started reading: send back the first data byte
+  Wire.write(shared_memory[memory_addr]);
+  memory_addr = next_addr(memory_addr);
+}
+
+// Function that executes each time the master in the current transaction
+// tries to read more than initially provided in the onRequest handler above.
+// This function is registered as an event, see setup()
+void requestMoreEvent() {
+  // master continues reading: send back the next data byte
+  Wire.write(shared_memory[memory_addr]);
+  memory_addr = next_addr(memory_addr);
+}